1 /* 2 * signalfd/eventfd compatibility 3 * 4 * Copyright IBM, Corp. 2008 5 * 6 * Authors: 7 * Anthony Liguori <aliguori (at) 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 27 struct sigfd_compat_info 28 { 29 sigset_t mask; 30 int fd; 31 }; 32 33 static 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 73 static 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 101 int 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 116 bool 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