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