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