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