xref: /illumos-kvm-cmd/compatfd.c (revision 68396ea9)
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 
20 struct sigfd_compat_info
21 {
22     sigset_t mask;
23     int fd;
24 };
25 
sigwait_compat(void * opaque)26 static void *sigwait_compat(void *opaque)
27 {
28     struct sigfd_compat_info *info = opaque;
29     int err;
30     sigset_t all;
31 
32     sigfillset(&all);
33     sigprocmask(SIG_BLOCK, &all, NULL);
34 
35     do {
36         siginfo_t siginfo;
37 
38         err = sigwaitinfo(&info->mask, &siginfo);
39         if (err == -1 && errno == EINTR) {
40             err = 0;
41             continue;
42         }
43 
44         if (err > 0) {
45             char buffer[128];
46             size_t offset = 0;
47 
48             memcpy(buffer, &err, sizeof(err));
49             while (offset < sizeof(buffer)) {
50                 ssize_t len;
51 
52                 len = write(info->fd, buffer + offset,
53                             sizeof(buffer) - offset);
54                 if (len == -1 && errno == EINTR)
55                     continue;
56 
57                 if (len <= 0) {
58                     err = -1;
59                     break;
60                 }
61 
62                 offset += len;
63             }
64         }
65     } while (err >= 0);
66 
67     return NULL;
68 }
69 
qemu_signalfd_compat(const sigset_t * mask)70 static 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 
qemu_signalfd(const sigset_t * mask)104 int 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