/* * signalfd/eventfd compatibility * * Copyright IBM, Corp. 2008 * * Authors: * Anthony Liguori <aliguori@us.ibm.com> * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. * * Contributions after 2012-01-13 are licensed under the terms of the * GNU GPL, version 2 or (at your option) any later version. */ #include "qemu-common.h" #include "compatfd.h" #include <sys/syscall.h> #include <pthread.h> struct sigfd_compat_info { sigset_t mask; int fd; }; static void *sigwait_compat(void *opaque) { struct sigfd_compat_info *info = opaque; sigset_t all; sigfillset(&all); pthread_sigmask(SIG_BLOCK, &all, NULL); while (1) { int sig; int err; err = sigwait(&info->mask, &sig); if (err != 0) { if (errno == EINTR) { continue; } else { return NULL; } } else { struct qemu_signalfd_siginfo buffer; size_t offset = 0; memset(&buffer, 0, sizeof(buffer)); buffer.ssi_signo = sig; while (offset < sizeof(buffer)) { ssize_t len; len = write(info->fd, (char *)&buffer + offset, sizeof(buffer) - offset); if (len == -1 && errno == EINTR) continue; if (len <= 0) { return NULL; } offset += len; } } } } static int qemu_signalfd_compat(const sigset_t *mask) { pthread_attr_t attr; pthread_t tid; struct sigfd_compat_info *info; int fds[2]; info = malloc(sizeof(*info)); if (info == NULL) { errno = ENOMEM; return -1; } if (pipe(fds) == -1) { free(info); return -1; } qemu_set_cloexec(fds[0]); qemu_set_cloexec(fds[1]); memcpy(&info->mask, mask, sizeof(*mask)); info->fd = fds[1]; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&tid, &attr, sigwait_compat, info); pthread_attr_destroy(&attr); return fds[0]; } int qemu_signalfd(const sigset_t *mask) { #if defined(CONFIG_SIGNALFD) int ret; ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8); if (ret != -1) { qemu_set_cloexec(ret); return ret; } #endif return qemu_signalfd_compat(mask); } bool qemu_signalfd_available(void) { #ifdef CONFIG_SIGNALFD sigset_t mask; int fd; bool ok; sigemptyset(&mask); errno = 0; fd = syscall(SYS_signalfd, -1, &mask, _NSIG / 8); ok = (errno != ENOSYS); if (fd >= 0) { close(fd); } return ok; #else return false; #endif }