2 * QEMU monitor file descriptor passing
4 * Copyright (c) 2003-2004 Fabrice Bellard
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
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"
34 /* file descriptors passed via SCM_RIGHTS */
35 typedef struct mon_fd_t mon_fd_t
;
39 QLIST_ENTRY(mon_fd_t
) next
;
42 /* file descriptor associated with a file descriptor set */
43 typedef struct MonFdsetFd MonFdsetFd
;
48 QLIST_ENTRY(MonFdsetFd
) next
;
51 /* file descriptor set containing fds passed via SCM_RIGHTS */
52 typedef struct MonFdset MonFdset
;
55 QLIST_HEAD(, MonFdsetFd
) fds
;
56 QLIST_HEAD(, MonFdsetFd
) dup_fds
;
57 QLIST_ENTRY(MonFdset
) next
;
60 /* Protects mon_fdsets */
61 static QemuMutex mon_fdsets_lock
;
62 static QLIST_HEAD(, MonFdset
) mon_fdsets
;
64 void qmp_getfd(const char *fdname
, Error
**errp
)
66 Monitor
*cur_mon
= monitor_cur();
70 fd
= qemu_chr_fe_get_msgfd(&cur_mon
->chr
);
72 error_setg(errp
, "No file descriptor supplied via SCM_RIGHTS");
76 if (qemu_isdigit(fdname
[0])) {
78 error_setg(errp
, QERR_INVALID_PARAMETER_VALUE
, "fdname",
79 "a name not starting with a digit");
83 QEMU_LOCK_GUARD(&cur_mon
->mon_lock
);
84 QLIST_FOREACH(monfd
, &cur_mon
->fds
, next
) {
85 if (strcmp(monfd
->name
, fdname
) != 0) {
91 /* Make sure close() is outside critical section */
96 monfd
= g_new0(mon_fd_t
, 1);
97 monfd
->name
= g_strdup(fdname
);
100 QLIST_INSERT_HEAD(&cur_mon
->fds
, monfd
, next
);
103 void qmp_closefd(const char *fdname
, Error
**errp
)
105 Monitor
*cur_mon
= monitor_cur();
109 qemu_mutex_lock(&cur_mon
->mon_lock
);
110 QLIST_FOREACH(monfd
, &cur_mon
->fds
, next
) {
111 if (strcmp(monfd
->name
, fdname
) != 0) {
115 QLIST_REMOVE(monfd
, next
);
119 qemu_mutex_unlock(&cur_mon
->mon_lock
);
120 /* Make sure close() is outside critical section */
125 qemu_mutex_unlock(&cur_mon
->mon_lock
);
126 error_setg(errp
, "File descriptor named '%s' not found", fdname
);
129 int monitor_get_fd(Monitor
*mon
, const char *fdname
, Error
**errp
)
133 QEMU_LOCK_GUARD(&mon
->mon_lock
);
134 QLIST_FOREACH(monfd
, &mon
->fds
, next
) {
137 if (strcmp(monfd
->name
, fdname
) != 0) {
144 /* caller takes ownership of fd */
145 QLIST_REMOVE(monfd
, next
);
152 error_setg(errp
, "File descriptor named '%s' has not been found", fdname
);
156 static void monitor_fdset_cleanup(MonFdset
*mon_fdset
)
158 MonFdsetFd
*mon_fdset_fd
;
159 MonFdsetFd
*mon_fdset_fd_next
;
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
);
172 if (QLIST_EMPTY(&mon_fdset
->fds
) && QLIST_EMPTY(&mon_fdset
->dup_fds
)) {
173 QLIST_REMOVE(mon_fdset
, next
);
178 void monitor_fdsets_cleanup(void)
181 MonFdset
*mon_fdset_next
;
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
);
189 AddfdInfo
*qmp_add_fd(bool has_fdset_id
, int64_t fdset_id
,
190 const char *opaque
, Error
**errp
)
193 Monitor
*mon
= monitor_cur();
196 fd
= qemu_chr_fe_get_msgfd(&mon
->chr
);
198 error_setg(errp
, "No file descriptor supplied via SCM_RIGHTS");
202 fdinfo
= monitor_fdset_add_fd(fd
, has_fdset_id
, fdset_id
, opaque
, errp
);
214 void qmp_remove_fd(int64_t fdset_id
, bool has_fd
, int64_t fd
, Error
**errp
)
217 MonFdsetFd
*mon_fdset_fd
;
220 QEMU_LOCK_GUARD(&mon_fdsets_lock
);
221 QLIST_FOREACH(mon_fdset
, &mon_fdsets
, next
) {
222 if (mon_fdset
->id
!= fdset_id
) {
225 QLIST_FOREACH(mon_fdset_fd
, &mon_fdset
->fds
, next
) {
227 if (mon_fdset_fd
->fd
!= fd
) {
230 mon_fdset_fd
->removed
= true;
233 mon_fdset_fd
->removed
= true;
236 if (has_fd
&& !mon_fdset_fd
) {
239 monitor_fdset_cleanup(mon_fdset
);
245 snprintf(fd_str
, sizeof(fd_str
), "fdset-id:%" PRId64
", fd:%" PRId64
,
248 snprintf(fd_str
, sizeof(fd_str
), "fdset-id:%" PRId64
, fdset_id
);
250 error_setg(errp
, "File descriptor named '%s' not found", fd_str
);
253 FdsetInfoList
*qmp_query_fdsets(Error
**errp
)
256 MonFdsetFd
*mon_fdset_fd
;
257 FdsetInfoList
*fdset_list
= NULL
;
259 QEMU_LOCK_GUARD(&mon_fdsets_lock
);
260 QLIST_FOREACH(mon_fdset
, &mon_fdsets
, next
) {
261 FdsetInfo
*fdset_info
= g_malloc0(sizeof(*fdset_info
));
263 fdset_info
->fdset_id
= mon_fdset
->id
;
265 QLIST_FOREACH(mon_fdset_fd
, &mon_fdset
->fds
, next
) {
266 FdsetFdInfo
*fdsetfd_info
;
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
);
272 QAPI_LIST_PREPEND(fdset_info
->fds
, fdsetfd_info
);
275 QAPI_LIST_PREPEND(fdset_list
, fdset_info
);
281 AddfdInfo
*monitor_fdset_add_fd(int fd
, bool has_fdset_id
, int64_t fdset_id
,
282 const char *opaque
, Error
**errp
)
284 MonFdset
*mon_fdset
= NULL
;
285 MonFdsetFd
*mon_fdset_fd
;
288 QEMU_LOCK_GUARD(&mon_fdsets_lock
);
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
) {
301 if (mon_fdset
== NULL
) {
302 int64_t fdset_id_prev
= -1;
303 MonFdset
*mon_fdset_cur
= QLIST_FIRST(&mon_fdsets
);
307 error_setg(errp
, QERR_INVALID_PARAMETER_VALUE
, "fdset-id",
308 "a non-negative value");
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
) {
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
;
330 mon_fdset
= g_malloc0(sizeof(*mon_fdset
));
332 mon_fdset
->id
= fdset_id
;
334 mon_fdset
->id
= fdset_id_prev
+ 1;
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
);
343 QLIST_INSERT_AFTER(mon_fdset_cur
, mon_fdset
, next
);
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
);
353 fdinfo
= g_malloc0(sizeof(*fdinfo
));
354 fdinfo
->fdset_id
= mon_fdset
->id
;
355 fdinfo
->fd
= mon_fdset_fd
->fd
;
360 int monitor_fdset_dup_fd_add(int64_t fdset_id
, int flags
)
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
;
375 if (mon_fdset
->id
!= fdset_id
) {
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) {
385 if ((flags
& O_ACCMODE
) == (mon_fd_flags
& O_ACCMODE
)) {
386 fd
= mon_fdset_fd
->fd
;
396 dup_fd
= qemu_dup_flags(fd
, flags
);
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
);
412 static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd
, bool remove
)
415 MonFdsetFd
*mon_fdset_fd_dup
;
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
) {
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
);
429 return mon_fdset
->id
;
438 int64_t monitor_fdset_dup_fd_find(int dup_fd
)
440 return monitor_fdset_dup_fd_find_remove(dup_fd
, false);
443 void monitor_fdset_dup_fd_remove(int dup_fd
)
445 monitor_fdset_dup_fd_find_remove(dup_fd
, true);
448 int monitor_fd_param(Monitor
*mon
, const char *fdname
, Error
**errp
)
452 if (!qemu_isdigit(fdname
[0]) && mon
) {
453 fd
= monitor_get_fd(mon
, fdname
, errp
);
455 fd
= qemu_parse_fd(fdname
);
457 error_setg(errp
, "Invalid file descriptor number '%s'",
465 static void __attribute__((__constructor__
)) monitor_fds_init(void)
467 qemu_mutex_init(&mon_fdsets_lock
);