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_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 192 void 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 203 AddfdInfo *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 221 error: 222 if (fd != -1) { 223 close(fd); 224 } 225 return NULL; 226 } 227 228 #ifdef WIN32 229 void 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 263 void 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 292 error: 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 302 FdsetInfoList *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 330 AddfdInfo *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 409 int 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 461 static 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 487 int64_t monitor_fdset_dup_fd_find(int dup_fd) 488 { 489 return monitor_fdset_dup_fd_find_remove(dup_fd, false); 490 } 491 492 void monitor_fdset_dup_fd_remove(int dup_fd) 493 { 494 monitor_fdset_dup_fd_find_remove(dup_fd, true); 495 } 496 497 int 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 514 static void __attribute__((__constructor__)) monitor_fds_init(void) 515 { 516 qemu_mutex_init(&mon_fdsets_lock); 517 } 518