xref: /openbmc/qemu/util/compatfd.c (revision 8917c3bd)
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