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