|  | /* | 
|  | * QEMU System Emulator - managing I/O handler | 
|  | * | 
|  | * Copyright (c) 2003-2008 Fabrice Bellard | 
|  | * | 
|  | * Permission is hereby granted, free of charge, to any person obtaining a copy | 
|  | * of this software and associated documentation files (the "Software"), to deal | 
|  | * in the Software without restriction, including without limitation the rights | 
|  | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|  | * copies of the Software, and to permit persons to whom the Software is | 
|  | * furnished to do so, subject to the following conditions: | 
|  | * | 
|  | * The above copyright notice and this permission notice shall be included in | 
|  | * all copies or substantial portions of the Software. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
|  | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|  | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|  | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
|  | * THE SOFTWARE. | 
|  | */ | 
|  |  | 
|  | #include "config-host.h" | 
|  | #include "qemu-common.h" | 
|  | #include "qemu/queue.h" | 
|  | #include "block/aio.h" | 
|  | #include "qemu/main-loop.h" | 
|  |  | 
|  | #ifndef _WIN32 | 
|  | #include <sys/wait.h> | 
|  | #endif | 
|  |  | 
|  | typedef struct IOHandlerRecord { | 
|  | IOHandler *fd_read; | 
|  | IOHandler *fd_write; | 
|  | void *opaque; | 
|  | QLIST_ENTRY(IOHandlerRecord) next; | 
|  | int fd; | 
|  | int pollfds_idx; | 
|  | bool deleted; | 
|  | } IOHandlerRecord; | 
|  |  | 
|  | static QLIST_HEAD(, IOHandlerRecord) io_handlers = | 
|  | QLIST_HEAD_INITIALIZER(io_handlers); | 
|  |  | 
|  | void qemu_set_fd_handler(int fd, | 
|  | IOHandler *fd_read, | 
|  | IOHandler *fd_write, | 
|  | void *opaque) | 
|  | { | 
|  | IOHandlerRecord *ioh; | 
|  |  | 
|  | assert(fd >= 0); | 
|  |  | 
|  | if (!fd_read && !fd_write) { | 
|  | QLIST_FOREACH(ioh, &io_handlers, next) { | 
|  | if (ioh->fd == fd) { | 
|  | ioh->deleted = 1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | QLIST_FOREACH(ioh, &io_handlers, next) { | 
|  | if (ioh->fd == fd) | 
|  | goto found; | 
|  | } | 
|  | ioh = g_malloc0(sizeof(IOHandlerRecord)); | 
|  | QLIST_INSERT_HEAD(&io_handlers, ioh, next); | 
|  | found: | 
|  | ioh->fd = fd; | 
|  | ioh->fd_read = fd_read; | 
|  | ioh->fd_write = fd_write; | 
|  | ioh->opaque = opaque; | 
|  | ioh->pollfds_idx = -1; | 
|  | ioh->deleted = 0; | 
|  | qemu_notify_event(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void qemu_iohandler_fill(GArray *pollfds) | 
|  | { | 
|  | IOHandlerRecord *ioh; | 
|  |  | 
|  | QLIST_FOREACH(ioh, &io_handlers, next) { | 
|  | int events = 0; | 
|  |  | 
|  | if (ioh->deleted) | 
|  | continue; | 
|  | if (ioh->fd_read) { | 
|  | events |= G_IO_IN | G_IO_HUP | G_IO_ERR; | 
|  | } | 
|  | if (ioh->fd_write) { | 
|  | events |= G_IO_OUT | G_IO_ERR; | 
|  | } | 
|  | if (events) { | 
|  | GPollFD pfd = { | 
|  | .fd = ioh->fd, | 
|  | .events = events, | 
|  | }; | 
|  | ioh->pollfds_idx = pollfds->len; | 
|  | g_array_append_val(pollfds, pfd); | 
|  | } else { | 
|  | ioh->pollfds_idx = -1; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void qemu_iohandler_poll(GArray *pollfds, int ret) | 
|  | { | 
|  | if (ret > 0) { | 
|  | IOHandlerRecord *pioh, *ioh; | 
|  |  | 
|  | QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { | 
|  | int revents = 0; | 
|  |  | 
|  | if (!ioh->deleted && ioh->pollfds_idx != -1) { | 
|  | GPollFD *pfd = &g_array_index(pollfds, GPollFD, | 
|  | ioh->pollfds_idx); | 
|  | revents = pfd->revents; | 
|  | } | 
|  |  | 
|  | if (!ioh->deleted && ioh->fd_read && | 
|  | (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) { | 
|  | ioh->fd_read(ioh->opaque); | 
|  | } | 
|  | if (!ioh->deleted && ioh->fd_write && | 
|  | (revents & (G_IO_OUT | G_IO_ERR))) { | 
|  | ioh->fd_write(ioh->opaque); | 
|  | } | 
|  |  | 
|  | /* Do this last in case read/write handlers marked it for deletion */ | 
|  | if (ioh->deleted) { | 
|  | QLIST_REMOVE(ioh, next); | 
|  | g_free(ioh); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* reaping of zombies.  right now we're not passing the status to | 
|  | anyone, but it would be possible to add a callback.  */ | 
|  | #ifndef _WIN32 | 
|  | typedef struct ChildProcessRecord { | 
|  | int pid; | 
|  | QLIST_ENTRY(ChildProcessRecord) next; | 
|  | } ChildProcessRecord; | 
|  |  | 
|  | static QLIST_HEAD(, ChildProcessRecord) child_watches = | 
|  | QLIST_HEAD_INITIALIZER(child_watches); | 
|  |  | 
|  | static QEMUBH *sigchld_bh; | 
|  |  | 
|  | static void sigchld_handler(int signal) | 
|  | { | 
|  | qemu_bh_schedule(sigchld_bh); | 
|  | } | 
|  |  | 
|  | static void sigchld_bh_handler(void *opaque) | 
|  | { | 
|  | ChildProcessRecord *rec, *next; | 
|  |  | 
|  | QLIST_FOREACH_SAFE(rec, &child_watches, next, next) { | 
|  | if (waitpid(rec->pid, NULL, WNOHANG) == rec->pid) { | 
|  | QLIST_REMOVE(rec, next); | 
|  | g_free(rec); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void qemu_init_child_watch(void) | 
|  | { | 
|  | struct sigaction act; | 
|  | sigchld_bh = qemu_bh_new(sigchld_bh_handler, NULL); | 
|  |  | 
|  | memset(&act, 0, sizeof(act)); | 
|  | act.sa_handler = sigchld_handler; | 
|  | act.sa_flags = SA_NOCLDSTOP; | 
|  | sigaction(SIGCHLD, &act, NULL); | 
|  | } | 
|  |  | 
|  | int qemu_add_child_watch(pid_t pid) | 
|  | { | 
|  | ChildProcessRecord *rec; | 
|  |  | 
|  | if (!sigchld_bh) { | 
|  | qemu_init_child_watch(); | 
|  | } | 
|  |  | 
|  | QLIST_FOREACH(rec, &child_watches, next) { | 
|  | if (rec->pid == pid) { | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | rec = g_malloc0(sizeof(ChildProcessRecord)); | 
|  | rec->pid = pid; | 
|  | QLIST_INSERT_HEAD(&child_watches, rec, next); | 
|  | return 0; | 
|  | } | 
|  | #endif |