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