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