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