1/* 2 * signalfd/eventfd compatibility 3 * 4 * Copyright IBM, Corp. 2008 5 * 6 * Authors: 7 * Anthony Liguori <aliguori@us.ibm.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2. See 10 * the COPYING file in the top-level directory. 11 * 12 * Contributions after 2012-01-13 are licensed under the terms of the 13 * GNU GPL, version 2 or (at your option) any later version. 14 */ 15 16// This is necessary to be able to include <sys/syscall.h> on Linux. 17#define _GNU_SOURCE 1 18 19#include "qemu-common.h" 20#include "qemu/compatfd.h" 21#include "qemu/thread.h" 22 23#ifdef CONFIG_SIGNALFD 24#include <sys/syscall.h> 25#endif 26 27struct sigfd_compat_info 28{ 29 sigset_t mask; 30 int fd; 31}; 32 33static void *sigwait_compat(void *opaque) 34{ 35 struct sigfd_compat_info *info = opaque; 36 37 while (1) { 38 int sig; 39 int err; 40 41 err = sigwait(&info->mask, &sig); 42 if (err != 0) { 43 if (errno == EINTR) { 44 continue; 45 } else { 46 return NULL; 47 } 48 } else { 49 struct qemu_signalfd_siginfo buffer; 50 size_t offset = 0; 51 52 memset(&buffer, 0, sizeof(buffer)); 53 buffer.ssi_signo = sig; 54 55 while (offset < sizeof(buffer)) { 56 ssize_t len; 57 58 len = write(info->fd, (char *)&buffer + offset, 59 sizeof(buffer) - offset); 60 if (len == -1 && errno == EINTR) 61 continue; 62 63 if (len <= 0) { 64 return NULL; 65 } 66 67 offset += len; 68 } 69 } 70 } 71} 72 73static int qemu_signalfd_compat(const sigset_t *mask) 74{ 75 struct sigfd_compat_info *info; 76 QemuThread thread; 77 int fds[2]; 78 79 info = malloc(sizeof(*info)); 80 if (info == NULL) { 81 errno = ENOMEM; 82 return -1; 83 } 84 85 if (pipe(fds) == -1) { 86 free(info); 87 return -1; 88 } 89 90 qemu_set_cloexec(fds[0]); 91 qemu_set_cloexec(fds[1]); 92 93 memcpy(&info->mask, mask, sizeof(*mask)); 94 info->fd = fds[1]; 95 96 qemu_thread_create(&thread, sigwait_compat, info, QEMU_THREAD_DETACHED); 97 98 return fds[0]; 99} 100 101int qemu_signalfd(const sigset_t *mask) 102{ 103#if defined(CONFIG_SIGNALFD) 104 int ret; 105 106 ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8); 107 if (ret != -1) { 108 qemu_set_cloexec(ret); 109 return ret; 110 } 111#endif 112 113 return qemu_signalfd_compat(mask); 114} 115 116bool qemu_signalfd_available(void) 117{ 118#ifdef CONFIG_SIGNALFD 119 sigset_t mask; 120 int fd; 121 bool ok; 122 sigemptyset(&mask); 123 errno = 0; 124 fd = syscall(SYS_signalfd, -1, &mask, _NSIG / 8); 125 ok = (errno != ENOSYS); 126 if (fd >= 0) { 127 close(fd); 128 } 129 return ok; 130#else 131 return false; 132#endif 133} 134