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 */ 13 14#include "qemu-common.h" 15#include "compatfd.h" 16 17#include <sys/syscall.h> 18#include <pthread.h> 19 20struct sigfd_compat_info 21{ 22 sigset_t mask; 23 int fd; 24}; 25 26static void *sigwait_compat(void *opaque) 27{ 28 struct sigfd_compat_info *info = opaque; 29 sigset_t all; 30 31 sigfillset(&all); 32 sigprocmask(SIG_BLOCK, &all, NULL); 33 34 while (1) { 35 int sig; 36 int err; 37 38 err = sigwait(&info->mask, &sig); 39 if (err != 0) { 40 if (errno == EINTR) { 41 continue; 42 } else { 43 return NULL; 44 } 45 } else { 46 struct qemu_signalfd_siginfo buffer; 47 size_t offset = 0; 48 49 memset(&buffer, 0, sizeof(buffer)); 50 buffer.ssi_signo = sig; 51 52 while (offset < sizeof(buffer)) { 53 ssize_t len; 54 55 len = write(info->fd, (char *)&buffer + offset, 56 sizeof(buffer) - offset); 57 if (len == -1 && errno == EINTR) 58 continue; 59 60 if (len <= 0) { 61 return NULL; 62 } 63 64 offset += len; 65 } 66 } 67 } 68} 69 70static int qemu_signalfd_compat(const sigset_t *mask) 71{ 72 pthread_attr_t attr; 73 pthread_t tid; 74 struct sigfd_compat_info *info; 75 int fds[2]; 76 77 info = malloc(sizeof(*info)); 78 if (info == NULL) { 79 errno = ENOMEM; 80 return -1; 81 } 82 83 if (pipe(fds) == -1) { 84 free(info); 85 return -1; 86 } 87 88 qemu_set_cloexec(fds[0]); 89 qemu_set_cloexec(fds[1]); 90 91 memcpy(&info->mask, mask, sizeof(*mask)); 92 info->fd = fds[1]; 93 94 pthread_attr_init(&attr); 95 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 96 97 pthread_create(&tid, &attr, sigwait_compat, info); 98 99 pthread_attr_destroy(&attr); 100 101 return fds[0]; 102} 103 104int qemu_signalfd(const sigset_t *mask) 105{ 106#if defined(CONFIG_SIGNALFD) 107 int ret; 108 109 ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8); 110 if (ret != -1) { 111 qemu_set_cloexec(ret); 112 return ret; 113 } 114#endif 115 116 return qemu_signalfd_compat(mask); 117} 118