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