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