xref: /openbmc/qemu/monitor/fds.c (revision ffeddb979400b1580ad28acbee09b6f971c3912d)
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  
monitor_add_fd(Monitor * mon,int fd,const char * fdname,Error ** errp)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
qmp_getfd(const char * fdname,Error ** errp)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  
qmp_closefd(const char * fdname,Error ** errp)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  
monitor_get_fd(Monitor * mon,const char * fdname,Error ** errp)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  
monitor_fdset_free(MonFdset * mon_fdset)169  static void monitor_fdset_free(MonFdset *mon_fdset)
170  {
171      QLIST_REMOVE(mon_fdset, next);
172      g_free(mon_fdset);
173  }
174  
monitor_fdset_free_if_empty(MonFdset * mon_fdset)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  
monitor_fdset_fd_free(MonFdsetFd * mon_fdset_fd)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  
monitor_fdsets_cleanup(void)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  
qmp_add_fd(bool has_fdset_id,int64_t fdset_id,const char * opaque,Error ** errp)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
qmp_get_win32_socket(const char * infos,const char * fdname,Error ** errp)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  
qmp_remove_fd(int64_t fdset_id,bool has_fd,int64_t fd,Error ** errp)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  
qmp_query_fdsets(Error ** errp)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  
monitor_fdset_add_fd(int fd,bool has_fdset_id,int64_t fdset_id,const char * opaque,Error ** errp)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  
monitor_fdset_dup_fd_add(int64_t fdset_id,int flags,Error ** errp)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  
monitor_fdset_dup_fd_remove(int dup_fd)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  
monitor_fd_param(Monitor * mon,const char * fdname,Error ** errp)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  
monitor_fds_init(void)512  static void __attribute__((__constructor__)) monitor_fds_init(void)
513  {
514      qemu_mutex_init(&mon_fdsets_lock);
515  }
516