diff options
-rw-r--r-- | compatfd.c | 128 | ||||
-rw-r--r-- | compatfd.h | 32 |
2 files changed, 160 insertions, 0 deletions
diff --git a/compatfd.c b/compatfd.c new file mode 100644 index 0000000000..46b0ae7ab7 --- /dev/null +++ b/compatfd.c @@ -0,0 +1,128 @@ +/* + * 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. + * + */ + +#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; + int err; + sigset_t all; + + sigfillset(&all); + sigprocmask(SIG_BLOCK, &all, NULL); + + do { + siginfo_t siginfo; + + err = sigwaitinfo(&info->mask, &siginfo); + if (err == -1 && errno == EINTR) { + err = 0; + continue; + } + + if (err > 0) { + char buffer[128]; + size_t offset = 0; + + memcpy(buffer, &err, sizeof(err)); + while (offset < sizeof(buffer)) { + ssize_t len; + + len = write(info->fd, buffer + offset, + sizeof(buffer) - offset); + if (len == -1 && errno == EINTR) + continue; + + if (len <= 0) { + err = -1; + break; + } + + offset += len; + } + } + } while (err >= 0); + + return NULL; +} + +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; + } + + 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(SYS_signalfd) + int ret; + + ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8); + if (!(ret == -1 && errno == ENOSYS)) + return ret; +#endif + + return qemu_signalfd_compat(mask); +} + +int qemu_eventfd(int *fds) +{ +#if defined(SYS_eventfd) + int ret; + + ret = syscall(SYS_eventfd, 0); + if (ret >= 0) { + fds[0] = fds[1] = ret; + return 0; + } else if (!(ret == -1 && errno == ENOSYS)) + return ret; +#endif + + return pipe(fds); +} diff --git a/compatfd.h b/compatfd.h new file mode 100644 index 0000000000..e7db2a1093 --- /dev/null +++ b/compatfd.h @@ -0,0 +1,32 @@ +/* + * 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. + * + */ + +#ifndef QEMU_COMPATFD_H +#define QEMU_COMPATFD_H + +#include <signal.h> + +#if defined(__linux__) && !defined(SYS_signalfd) +struct signalfd_siginfo { + uint32_t ssi_signo; + uint8_t pad[124]; +}; +#else +#include <linux/signalfd.h> +#endif + +int qemu_signalfd(const sigset_t *mask); + +int qemu_eventfd(int *fds); + +#endif |