xref: /openbmc/qemu/monitor/fds.c (revision 87d67fad)
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_free(MonFdset *mon_fdset)
171 {
172     QLIST_REMOVE(mon_fdset, next);
173     g_free(mon_fdset);
174 }
175 
176 static void monitor_fdset_free_if_empty(MonFdset *mon_fdset)
177 {
178     /*
179      * Only remove an empty fdset. The fds are owned by the user and
180      * should have been removed with qmp_remove_fd(). The dup_fds are
181      * owned by QEMU and should have been removed with qemu_close().
182      */
183     if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) {
184         monitor_fdset_free(mon_fdset);
185     }
186 }
187 
188 static void monitor_fdset_fd_free(MonFdsetFd *mon_fdset_fd)
189 {
190     close(mon_fdset_fd->fd);
191     g_free(mon_fdset_fd->opaque);
192     QLIST_REMOVE(mon_fdset_fd, next);
193     g_free(mon_fdset_fd);
194 }
195 
196 static void monitor_fdset_cleanup(MonFdset *mon_fdset)
197 {
198     MonFdsetFd *mon_fdset_fd;
199     MonFdsetFd *mon_fdset_fd_next;
200 
201     QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) {
202         if (mon_fdset_fd->removed) {
203             monitor_fdset_fd_free(mon_fdset_fd);
204         }
205     }
206 
207     monitor_fdset_free_if_empty(mon_fdset);
208 }
209 
210 void monitor_fdsets_cleanup(void)
211 {
212     MonFdset *mon_fdset;
213     MonFdset *mon_fdset_next;
214 
215     QEMU_LOCK_GUARD(&mon_fdsets_lock);
216     QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) {
217         monitor_fdset_free_if_empty(mon_fdset);
218     }
219 }
220 
221 AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id,
222                       const char *opaque, Error **errp)
223 {
224     int fd;
225     Monitor *mon = monitor_cur();
226     AddfdInfo *fdinfo;
227 
228     fd = qemu_chr_fe_get_msgfd(&mon->chr);
229     if (fd == -1) {
230         error_setg(errp, "No file descriptor supplied via SCM_RIGHTS");
231         goto error;
232     }
233 
234     fdinfo = monitor_fdset_add_fd(fd, has_fdset_id, fdset_id, opaque, errp);
235     if (fdinfo) {
236         return fdinfo;
237     }
238 
239 error:
240     if (fd != -1) {
241         close(fd);
242     }
243     return NULL;
244 }
245 
246 #ifdef WIN32
247 void qmp_get_win32_socket(const char *infos, const char *fdname, Error **errp)
248 {
249     g_autofree WSAPROTOCOL_INFOW *info = NULL;
250     gsize len;
251     SOCKET sk;
252     int fd;
253 
254     info = (void *)g_base64_decode(infos, &len);
255     if (len != sizeof(*info)) {
256         error_setg(errp, "Invalid WSAPROTOCOL_INFOW value");
257         return;
258     }
259 
260     sk = WSASocketW(FROM_PROTOCOL_INFO,
261                     FROM_PROTOCOL_INFO,
262                     FROM_PROTOCOL_INFO,
263                     info, 0, 0);
264     if (sk == INVALID_SOCKET) {
265         error_setg_win32(errp, WSAGetLastError(), "Couldn't import socket");
266         return;
267     }
268 
269     fd = _open_osfhandle(sk, _O_BINARY);
270     if (fd < 0) {
271         error_setg_errno(errp, errno, "Failed to associate a FD with the SOCKET");
272         closesocket(sk);
273         return;
274     }
275 
276     monitor_add_fd(monitor_cur(), fd, fdname, errp);
277 }
278 #endif
279 
280 
281 void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp)
282 {
283     MonFdset *mon_fdset;
284     MonFdsetFd *mon_fdset_fd;
285     char fd_str[60];
286 
287     QEMU_LOCK_GUARD(&mon_fdsets_lock);
288     QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
289         if (mon_fdset->id != fdset_id) {
290             continue;
291         }
292         QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
293             if (has_fd) {
294                 if (mon_fdset_fd->fd != fd) {
295                     continue;
296                 }
297                 mon_fdset_fd->removed = true;
298                 break;
299             } else {
300                 mon_fdset_fd->removed = true;
301             }
302         }
303         if (has_fd && !mon_fdset_fd) {
304             goto error;
305         }
306         monitor_fdset_cleanup(mon_fdset);
307         return;
308     }
309 
310 error:
311     if (has_fd) {
312         snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64,
313                  fdset_id, fd);
314     } else {
315         snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64, fdset_id);
316     }
317     error_setg(errp, "File descriptor named '%s' not found", fd_str);
318 }
319 
320 FdsetInfoList *qmp_query_fdsets(Error **errp)
321 {
322     MonFdset *mon_fdset;
323     MonFdsetFd *mon_fdset_fd;
324     FdsetInfoList *fdset_list = NULL;
325 
326     QEMU_LOCK_GUARD(&mon_fdsets_lock);
327     QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
328         FdsetInfo *fdset_info = g_malloc0(sizeof(*fdset_info));
329 
330         fdset_info->fdset_id = mon_fdset->id;
331 
332         QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
333             FdsetFdInfo *fdsetfd_info;
334 
335             fdsetfd_info = g_malloc0(sizeof(*fdsetfd_info));
336             fdsetfd_info->fd = mon_fdset_fd->fd;
337             fdsetfd_info->opaque = g_strdup(mon_fdset_fd->opaque);
338 
339             QAPI_LIST_PREPEND(fdset_info->fds, fdsetfd_info);
340         }
341 
342         QAPI_LIST_PREPEND(fdset_list, fdset_info);
343     }
344 
345     return fdset_list;
346 }
347 
348 AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
349                                 const char *opaque, Error **errp)
350 {
351     MonFdset *mon_fdset = NULL;
352     MonFdsetFd *mon_fdset_fd;
353     AddfdInfo *fdinfo;
354 
355     QEMU_LOCK_GUARD(&mon_fdsets_lock);
356     if (has_fdset_id) {
357         QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
358             /* Break if match found or match impossible due to ordering by ID */
359             if (fdset_id <= mon_fdset->id) {
360                 if (fdset_id < mon_fdset->id) {
361                     mon_fdset = NULL;
362                 }
363                 break;
364             }
365         }
366     }
367 
368     if (mon_fdset == NULL) {
369         int64_t fdset_id_prev = -1;
370         MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets);
371 
372         if (has_fdset_id) {
373             if (fdset_id < 0) {
374                 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id",
375                            "a non-negative value");
376                 return NULL;
377             }
378             /* Use specified fdset ID */
379             QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
380                 mon_fdset_cur = mon_fdset;
381                 if (fdset_id < mon_fdset_cur->id) {
382                     break;
383                 }
384             }
385         } else {
386             /* Use first available fdset ID */
387             QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
388                 mon_fdset_cur = mon_fdset;
389                 if (fdset_id_prev == mon_fdset_cur->id - 1) {
390                     fdset_id_prev = mon_fdset_cur->id;
391                     continue;
392                 }
393                 break;
394             }
395         }
396 
397         mon_fdset = g_malloc0(sizeof(*mon_fdset));
398         if (has_fdset_id) {
399             mon_fdset->id = fdset_id;
400         } else {
401             mon_fdset->id = fdset_id_prev + 1;
402         }
403 
404         /* The fdset list is ordered by fdset ID */
405         if (!mon_fdset_cur) {
406             QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next);
407         } else if (mon_fdset->id < mon_fdset_cur->id) {
408             QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next);
409         } else {
410             QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next);
411         }
412     }
413 
414     mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd));
415     mon_fdset_fd->fd = fd;
416     mon_fdset_fd->removed = false;
417     mon_fdset_fd->opaque = g_strdup(opaque);
418     QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next);
419 
420     fdinfo = g_malloc0(sizeof(*fdinfo));
421     fdinfo->fdset_id = mon_fdset->id;
422     fdinfo->fd = mon_fdset_fd->fd;
423 
424     return fdinfo;
425 }
426 
427 int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags)
428 {
429 #ifdef _WIN32
430     return -ENOENT;
431 #else
432     MonFdset *mon_fdset;
433 
434     QEMU_LOCK_GUARD(&mon_fdsets_lock);
435     QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
436         MonFdsetFd *mon_fdset_fd;
437         MonFdsetFd *mon_fdset_fd_dup;
438         int fd = -1;
439         int dup_fd;
440         int mon_fd_flags;
441 
442         if (mon_fdset->id != fdset_id) {
443             continue;
444         }
445 
446         QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
447             mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL);
448             if (mon_fd_flags == -1) {
449                 return -1;
450             }
451 
452             if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) {
453                 fd = mon_fdset_fd->fd;
454                 break;
455             }
456         }
457 
458         if (fd == -1) {
459             errno = EACCES;
460             return -1;
461         }
462 
463         dup_fd = qemu_dup_flags(fd, flags);
464         if (dup_fd == -1) {
465             return -1;
466         }
467 
468         mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup));
469         mon_fdset_fd_dup->fd = dup_fd;
470         QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next);
471         return dup_fd;
472     }
473 
474     errno = ENOENT;
475     return -1;
476 #endif
477 }
478 
479 void monitor_fdset_dup_fd_remove(int dup_fd)
480 {
481     MonFdset *mon_fdset;
482     MonFdsetFd *mon_fdset_fd_dup;
483 
484     QEMU_LOCK_GUARD(&mon_fdsets_lock);
485     QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
486         QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) {
487             if (mon_fdset_fd_dup->fd == dup_fd) {
488                 QLIST_REMOVE(mon_fdset_fd_dup, next);
489                 g_free(mon_fdset_fd_dup);
490                 monitor_fdset_free_if_empty(mon_fdset);
491                 return;
492             }
493         }
494     }
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