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