qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] linux-user: add signalfd/signalfd4 syscalls


From: Laurent Vivier
Subject: [Qemu-devel] [PATCH] linux-user: add signalfd/signalfd4 syscalls
Date: Sat, 30 May 2015 00:50:58 +0200

This patch introduces a system very similar to the one used in the kernel
to attach specific functions to a given file descriptor.

In this case, we attach a specific "read()" to the fd returned by
signalfd() to be able to byte-swap the signalfd_siginfo structure
provided by read().

This system could be also used to introduce netlink sockets.

This patch allows to execute the example program given by
man signalfd(2):

    do { perror(msg); exit(EXIT_FAILURE); } while (0)

int
main(int argc, char *argv[])
{
    sigset_t mask;
    int sfd;
    struct signalfd_siginfo fdsi;
    ssize_t s;

    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGQUIT);

    /* Block signals so that they aren't handled
       according to their default dispositions */

    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
        handle_error("sigprocmask");

    sfd = signalfd(-1, &mask, 0);
    if (sfd == -1)
        handle_error("signalfd");

    for (;;) {
        s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
        if (s != sizeof(struct signalfd_siginfo))
            handle_error("read");

        if (fdsi.ssi_signo == SIGINT) {
            printf("Got SIGINT\n");
        } else if (fdsi.ssi_signo == SIGQUIT) {
            printf("Got SIGQUIT\n");
            exit(EXIT_SUCCESS);
        } else {
            printf("Read unexpected signal\n");
        }
    }
}

$ ./signalfd_demo
^CGot SIGINT
^CGot SIGINT
^\Got SIGQUIT

Signed-off-by: Laurent Vivier <address@hidden>
---
 linux-user/syscall.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 116 insertions(+), 1 deletion(-)

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 1622ad6..c72c440 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -60,6 +60,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
 #include <sys/statfs.h>
 #include <utime.h>
 #include <sys/sysinfo.h>
+#include <sys/signalfd.h>
 //#include <sys/user.h>
 #include <netinet/ip.h>
 #include <netinet/tcp.h>
@@ -294,6 +295,15 @@ static bitmask_transtbl fcntl_flags_tbl[] = {
   { 0, 0, 0, 0 }
 };
 
+struct target_fd_ops {
+    abi_long (*read) (int, void *, size_t);
+};
+
+typedef struct target_fd_ops target_fd_ops_t;
+
+static int target_fd_max;
+static target_fd_ops_t **target_fd_ops;
+
 static int sys_getcwd1(char *buf, size_t size)
 {
   if (getcwd(buf, size) == NULL) {
@@ -4878,6 +4888,7 @@ void syscall_init(void)
     const argtype *arg_type;
     int size;
     int i;
+    struct rlimit rlim;
 
 #define STRUCT(name, ...) thunk_register_struct(STRUCT_ ## name, #name, 
struct_ ## name ## _def);
 #define STRUCT_SPECIAL(name) thunk_register_struct_direct(STRUCT_ ## name, 
#name, &struct_ ## name ## _def);
@@ -4885,6 +4896,14 @@ void syscall_init(void)
 #undef STRUCT
 #undef STRUCT_SPECIAL
 
+    /* allocate an array to store fd ops */
+
+    if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
+        target_fd_max = rlim.rlim_cur;
+        target_fd_ops = g_malloc0(target_fd_max * sizeof(target_fd_ops_t *));
+    }
+
+
     /* Build target_to_host_errno_table[] table from
      * host_to_target_errno_table[]. */
     for (i = 0; i < ERRNO_TABLE_SIZE; i++) {
@@ -5512,6 +5531,51 @@ static target_timer_t get_timer_id(abi_long arg)
     return timerid;
 }
 
+/* signalfd siginfo conversion */
+
+static void
+host_to_target_signalfd_siginfo(struct signalfd_siginfo *tinfo,
+                                const struct signalfd_siginfo *info)
+{
+    int sig = host_to_target_signal(info->ssi_signo);
+    tinfo->ssi_signo = tswap32(sig);
+    tinfo->ssi_errno = 0; /* unused */
+    tinfo->ssi_code = tswap32(info->ssi_code);
+    tinfo->ssi_pid =  tswap32(info->ssi_pid);
+    tinfo->ssi_uid =  tswap32(info->ssi_uid);
+    tinfo->ssi_fd =  tswap32(info->ssi_fd);
+    tinfo->ssi_tid =  tswap32(info->ssi_tid);
+    tinfo->ssi_band =  tswap32(info->ssi_band);
+    tinfo->ssi_overrun =  tswap32(info->ssi_overrun);
+    tinfo->ssi_trapno =  tswap32(info->ssi_trapno);
+    tinfo->ssi_status =  tswap32(info->ssi_status);
+    tinfo->ssi_int =  tswap32(info->ssi_int);
+    tinfo->ssi_ptr =  tswap64(info->ssi_ptr);
+    tinfo->ssi_utime =  tswap64(info->ssi_utime);
+    tinfo->ssi_stime =  tswap64(info->ssi_stime);
+    tinfo->ssi_addr =  tswap64(info->ssi_addr);
+}
+
+static abi_long target_signalfd_read(int fd, void *buf, size_t count)
+{
+    int ret;
+    int i;
+
+    ret = get_errno(read(fd, buf, count));
+    if (ret < 0) {
+        return ret;
+    }
+
+    for (i = 0; i < ret; i += sizeof(struct signalfd_siginfo)) {
+        host_to_target_signalfd_siginfo(buf + i, buf + i);
+    }
+    return ret;
+}
+
+static target_fd_ops_t target_signalfd_ops = {
+    .read = target_signalfd_read,
+};
+
 /* do_syscall() should always have a single exit point at the end so
    that actions, such as logging of syscall results, can be performed.
    All errnos that do_syscall() returns must be -TARGET_<errcode>. */
@@ -5571,7 +5635,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long 
arg1,
         else {
             if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
                 goto efault;
-            ret = get_errno(read(arg1, p, arg3));
+            if (arg1 < target_fd_max &&
+                target_fd_ops[arg1] &&
+                target_fd_ops[arg1]->read) {
+                ret =  target_fd_ops[arg1]->read(arg1, p, arg3);
+            } else {
+                ret = get_errno(read(arg1, p, arg3));
+            }
             unlock_user(p, arg2, ret);
         }
         break;
@@ -5598,6 +5668,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long 
arg1,
         unlock_user(p, arg2, 0);
         break;
     case TARGET_NR_close:
+        if (arg1 < target_fd_max &&
+            target_fd_ops[arg1]) {
+            target_fd_ops[arg1] = NULL;
+        }
         ret = get_errno(close(arg1));
         break;
     case TARGET_NR_brk:
@@ -9451,6 +9525,26 @@ abi_long do_syscall(void *cpu_env, int num, abi_long 
arg1,
         break;
 #endif
 #endif
+#if defined(TARGET_NR_signalfd4)
+    case TARGET_NR_signalfd4:
+        {
+            target_sigset_t *target_mask;
+            sigset_t mask;
+            if (!lock_user_struct(VERIFY_READ, target_mask, arg2, 1)) {
+                goto efault;
+            }
+
+            target_to_host_sigset(&mask, target_mask);
+
+            ret = get_errno(signalfd(arg1, &mask, arg4));
+            if (ret >= 0) {
+                target_fd_ops[ret] = &target_signalfd_ops;
+            }
+
+            unlock_user_struct(target_mask, arg2, 0);
+        }
+        break;
+#endif
 #if defined(CONFIG_EPOLL)
 #if defined(TARGET_NR_epoll_create)
     case TARGET_NR_epoll_create:
@@ -9743,6 +9837,27 @@ abi_long do_syscall(void *cpu_env, int num, abi_long 
arg1,
     }
 #endif
 
+#if defined(TARGET_NR_signalfd)
+    case TARGET_NR_signalfd:
+        {
+            target_sigset_t *target_mask;
+            sigset_t mask;
+            if (!lock_user_struct(VERIFY_READ, target_mask, arg2, 1)) {
+                goto efault;
+            }
+
+            target_to_host_sigset(&mask, target_mask);
+
+            ret = get_errno(signalfd(arg1, &mask, arg3));
+            if (ret >= 0) {
+                target_fd_ops[ret] = &target_signalfd_ops;
+            }
+
+            unlock_user_struct(target_mask, arg2, 0);
+        }
+        break;
+#endif
+
 #if defined(TARGET_NR_timerfd_create) && defined(CONFIG_TIMERFD)
     case TARGET_NR_timerfd_create:
         ret = get_errno(timerfd_create(arg1,
-- 
2.4.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]