Home | History | Annotate | Download | only in util
      1 /*
      2  * signalfd/eventfd compatibility
      3  *
      4  * Copyright IBM, Corp. 2008
      5  *
      6  * Authors:
      7  *  Anthony Liguori   <aliguori (at) 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 // This is necessary to be able to include <sys/syscall.h> on Linux.
     17 #define _GNU_SOURCE 1
     18 
     19 #include "qemu-common.h"
     20 #include "qemu/compatfd.h"
     21 #include "qemu/thread.h"
     22 
     23 #ifdef CONFIG_SIGNALFD
     24 #include <sys/syscall.h>
     25 #endif
     26 
     27 struct sigfd_compat_info
     28 {
     29     sigset_t mask;
     30     int fd;
     31 };
     32 
     33 static void *sigwait_compat(void *opaque)
     34 {
     35     struct sigfd_compat_info *info = opaque;
     36 
     37     while (1) {
     38         int sig;
     39         int err;
     40 
     41         err = sigwait(&info->mask, &sig);
     42         if (err != 0) {
     43             if (errno == EINTR) {
     44                 continue;
     45             } else {
     46                 return NULL;
     47             }
     48         } else {
     49             struct qemu_signalfd_siginfo buffer;
     50             size_t offset = 0;
     51 
     52             memset(&buffer, 0, sizeof(buffer));
     53             buffer.ssi_signo = sig;
     54 
     55             while (offset < sizeof(buffer)) {
     56                 ssize_t len;
     57 
     58                 len = write(info->fd, (char *)&buffer + offset,
     59                             sizeof(buffer) - offset);
     60                 if (len == -1 && errno == EINTR)
     61                     continue;
     62 
     63                 if (len <= 0) {
     64                     return NULL;
     65                 }
     66 
     67                 offset += len;
     68             }
     69         }
     70     }
     71 }
     72 
     73 static int qemu_signalfd_compat(const sigset_t *mask)
     74 {
     75     struct sigfd_compat_info *info;
     76     QemuThread thread;
     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     qemu_thread_create(&thread, sigwait_compat, info, QEMU_THREAD_DETACHED);
     97 
     98     return fds[0];
     99 }
    100 
    101 int qemu_signalfd(const sigset_t *mask)
    102 {
    103 #if defined(CONFIG_SIGNALFD)
    104     int ret;
    105 
    106     ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8);
    107     if (ret != -1) {
    108         qemu_set_cloexec(ret);
    109         return ret;
    110     }
    111 #endif
    112 
    113     return qemu_signalfd_compat(mask);
    114 }
    115 
    116 bool qemu_signalfd_available(void)
    117 {
    118 #ifdef CONFIG_SIGNALFD
    119     sigset_t mask;
    120     int fd;
    121     bool ok;
    122     sigemptyset(&mask);
    123     errno = 0;
    124     fd = syscall(SYS_signalfd, -1, &mask, _NSIG / 8);
    125     ok = (errno != ENOSYS);
    126     if (fd >= 0) {
    127         close(fd);
    128     }
    129     return ok;
    130 #else
    131     return false;
    132 #endif
    133 }
    134