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 #include "qemu-common.h" 17 #include "qemu/compatfd.h" 18 19 #include <sys/syscall.h> 20 #include <pthread.h> 21 22 struct sigfd_compat_info 23 { 24 sigset_t mask; 25 int fd; 26 }; 27 28 static void *sigwait_compat(void *opaque) 29 { 30 struct sigfd_compat_info *info = opaque; 31 sigset_t all; 32 33 sigfillset(&all); 34 pthread_sigmask(SIG_BLOCK, &all, NULL); 35 36 while (1) { 37 int sig; 38 int err; 39 40 err = sigwait(&info->mask, &sig); 41 if (err != 0) { 42 if (errno == EINTR) { 43 continue; 44 } else { 45 return NULL; 46 } 47 } else { 48 struct qemu_signalfd_siginfo buffer; 49 size_t offset = 0; 50 51 memset(&buffer, 0, sizeof(buffer)); 52 buffer.ssi_signo = sig; 53 54 while (offset < sizeof(buffer)) { 55 ssize_t len; 56 57 len = write(info->fd, (char *)&buffer + offset, 58 sizeof(buffer) - offset); 59 if (len == -1 && errno == EINTR) 60 continue; 61 62 if (len <= 0) { 63 return NULL; 64 } 65 66 offset += len; 67 } 68 } 69 } 70 } 71 72 static int qemu_signalfd_compat(const sigset_t *mask) 73 { 74 pthread_attr_t attr; 75 pthread_t tid; 76 struct sigfd_compat_info *info; 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 pthread_attr_init(&attr); 97 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 98 99 pthread_create(&tid, &attr, sigwait_compat, info); 100 101 pthread_attr_destroy(&attr); 102 103 return fds[0]; 104 } 105 106 int qemu_signalfd(const sigset_t *mask) 107 { 108 #if defined(CONFIG_SIGNALFD) 109 int ret; 110 111 ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8); 112 if (ret != -1) { 113 qemu_set_cloexec(ret); 114 return ret; 115 } 116 #endif 117 118 return qemu_signalfd_compat(mask); 119 } 120 121 bool qemu_signalfd_available(void) 122 { 123 #ifdef CONFIG_SIGNALFD 124 sigset_t mask; 125 int fd; 126 bool ok; 127 sigemptyset(&mask); 128 errno = 0; 129 fd = syscall(SYS_signalfd, -1, &mask, _NSIG / 8); 130 ok = (errno != ENOSYS); 131 if (fd >= 0) { 132 close(fd); 133 } 134 return ok; 135 #else 136 return false; 137 #endif 138 } 139