From patchwork Fri May 29 22:50:58 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Vivier X-Patchwork-Id: 478072 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id BB39F140E61 for ; Sat, 30 May 2015 08:51:27 +1000 (AEST) Received: from localhost ([::1]:37894 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YyT7b-0000n2-6c for incoming@patchwork.ozlabs.org; Fri, 29 May 2015 18:51:23 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:44904) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YyT7N-0000Wg-3Q for qemu-devel@nongnu.org; Fri, 29 May 2015 18:51:10 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YyT7J-0000Cy-2D for qemu-devel@nongnu.org; Fri, 29 May 2015 18:51:09 -0400 Received: from smtp4-g21.free.fr ([2a01:e0c:1:1599::13]:21094) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YyT7I-0000Cu-Oh for qemu-devel@nongnu.org; Fri, 29 May 2015 18:51:04 -0400 Received: from Quad.localdomain (unknown [78.238.229.36]) by smtp4-g21.free.fr (Postfix) with ESMTPS id 447054C801D; Sat, 30 May 2015 00:47:08 +0200 (CEST) From: Laurent Vivier To: Riku Voipio Date: Sat, 30 May 2015 00:50:58 +0200 Message-Id: <1432939858-31952-1-git-send-email-laurent@vivier.eu> X-Mailer: git-send-email 2.4.1 X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2a01:e0c:1:1599::13 Cc: qemu-devel@nongnu.org, Laurent Vivier Subject: [Qemu-devel] [PATCH] linux-user: add signalfd/signalfd4 syscalls X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- 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 #include #include +#include //#include #include #include @@ -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_. */ @@ -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,