/* * 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 #endif typedef struct IOHandlerRecord { IOCanReadHandler *fd_read_poll; 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); /* XXX: fd_read_poll should be suppressed, but an API change is necessary in the character devices to suppress fd_can_read(). */ int qemu_set_fd_handler2(int fd, IOCanReadHandler *fd_read_poll, 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_poll = fd_read_poll; ioh->fd_read = fd_read; ioh->fd_write = fd_write; ioh->opaque = opaque; ioh->pollfds_idx = -1; ioh->deleted = 0; qemu_notify_event(); } return 0; } int qemu_set_fd_handler(int fd, IOHandler *fd_read, IOHandler *fd_write, void *opaque) { return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque); } 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 && (!ioh->fd_read_poll || ioh->fd_read_poll(ioh->opaque) != 0)) { 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); 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