linux-user: add signalfd/signalfd4 syscalls
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 "host_to_target()" translator to the fd returned by signalfd() to be able to byte-swap the signalfd_siginfo structure provided by read(). This patch allows to execute the example program given by man signalfd(2): #include <sys/signalfd.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #define handle_error(msg) \ 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 <laurent@vivier.eu> Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
This commit is contained in:
parent
38a762fec6
commit
e36800c91a
@ -60,6 +60,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
|
|||||||
#include <sys/statfs.h>
|
#include <sys/statfs.h>
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
#include <sys/sysinfo.h>
|
#include <sys/sysinfo.h>
|
||||||
|
#include <sys/signalfd.h>
|
||||||
//#include <sys/user.h>
|
//#include <sys/user.h>
|
||||||
#include <netinet/ip.h>
|
#include <netinet/ip.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
@ -294,6 +295,54 @@ static bitmask_transtbl fcntl_flags_tbl[] = {
|
|||||||
{ 0, 0, 0, 0 }
|
{ 0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef abi_long (*TargetFdFunc)(void *, size_t);
|
||||||
|
typedef struct TargetFdTrans {
|
||||||
|
TargetFdFunc host_to_target;
|
||||||
|
TargetFdFunc target_to_host;
|
||||||
|
} TargetFdTrans;
|
||||||
|
|
||||||
|
static TargetFdTrans **target_fd_trans;
|
||||||
|
|
||||||
|
static unsigned int target_fd_max;
|
||||||
|
|
||||||
|
static TargetFdFunc fd_trans_host_to_target(int fd)
|
||||||
|
{
|
||||||
|
if (fd < target_fd_max && target_fd_trans[fd]) {
|
||||||
|
return target_fd_trans[fd]->host_to_target;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fd_trans_register(int fd, TargetFdTrans *trans)
|
||||||
|
{
|
||||||
|
unsigned int oldmax;
|
||||||
|
|
||||||
|
if (fd >= target_fd_max) {
|
||||||
|
oldmax = target_fd_max;
|
||||||
|
target_fd_max = ((fd >> 6) + 1) << 6; /* by slice of 64 entries */
|
||||||
|
target_fd_trans = g_realloc(target_fd_trans,
|
||||||
|
target_fd_max * sizeof(TargetFdTrans));
|
||||||
|
memset((void *)(target_fd_trans + oldmax), 0,
|
||||||
|
(target_fd_max - oldmax) * sizeof(TargetFdTrans *));
|
||||||
|
}
|
||||||
|
target_fd_trans[fd] = trans;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fd_trans_unregister(int fd)
|
||||||
|
{
|
||||||
|
if (fd >= 0 && fd < target_fd_max) {
|
||||||
|
target_fd_trans[fd] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fd_trans_dup(int oldfd, int newfd)
|
||||||
|
{
|
||||||
|
fd_trans_unregister(newfd);
|
||||||
|
if (oldfd < target_fd_max && target_fd_trans[oldfd]) {
|
||||||
|
fd_trans_register(newfd, target_fd_trans[oldfd]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int sys_getcwd1(char *buf, size_t size)
|
static int sys_getcwd1(char *buf, size_t size)
|
||||||
{
|
{
|
||||||
if (getcwd(buf, size) == NULL) {
|
if (getcwd(buf, size) == NULL) {
|
||||||
@ -5340,6 +5389,92 @@ static abi_long do_open_by_handle_at(abi_long mount_fd, abi_long handle,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(TARGET_NR_signalfd) || defined(TARGET_NR_signalfd4)
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* linux/signalfd.h defines a ssi_addr_lsb
|
||||||
|
* not defined in sys/signalfd.h but used by some kernels
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef BUS_MCEERR_AO
|
||||||
|
if (tinfo->ssi_signo == SIGBUS &&
|
||||||
|
(tinfo->ssi_code == BUS_MCEERR_AR ||
|
||||||
|
tinfo->ssi_code == BUS_MCEERR_AO)) {
|
||||||
|
uint16_t *ssi_addr_lsb = (uint16_t *)(&info->ssi_addr + 1);
|
||||||
|
uint16_t *tssi_addr_lsb = (uint16_t *)(&tinfo->ssi_addr + 1);
|
||||||
|
*tssi_addr_lsb = tswap16(*ssi_addr_lsb);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
tinfo->ssi_signo = tswap32(sig);
|
||||||
|
tinfo->ssi_errno = tswap32(tinfo->ssi_errno);
|
||||||
|
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 host_to_target_signalfd(void *buf, size_t len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i += sizeof(struct signalfd_siginfo)) {
|
||||||
|
host_to_target_signalfd_siginfo(buf + i, buf + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TargetFdTrans target_signalfd_trans = {
|
||||||
|
.host_to_target = host_to_target_signalfd,
|
||||||
|
};
|
||||||
|
|
||||||
|
static abi_long do_signalfd4(int fd, abi_long mask, int flags)
|
||||||
|
{
|
||||||
|
int host_flags;
|
||||||
|
target_sigset_t *target_mask;
|
||||||
|
sigset_t host_mask;
|
||||||
|
abi_long ret;
|
||||||
|
|
||||||
|
if (flags & ~(TARGET_O_NONBLOCK | TARGET_O_CLOEXEC)) {
|
||||||
|
return -TARGET_EINVAL;
|
||||||
|
}
|
||||||
|
if (!lock_user_struct(VERIFY_READ, target_mask, mask, 1)) {
|
||||||
|
return -TARGET_EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_to_host_sigset(&host_mask, target_mask);
|
||||||
|
|
||||||
|
host_flags = target_to_host_bitmask(flags, fcntl_flags_tbl);
|
||||||
|
|
||||||
|
ret = get_errno(signalfd(fd, &host_mask, host_flags));
|
||||||
|
if (ret >= 0) {
|
||||||
|
fd_trans_register(ret, &target_signalfd_trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_user_struct(target_mask, mask, 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Map host to target signal numbers for the wait family of syscalls.
|
/* Map host to target signal numbers for the wait family of syscalls.
|
||||||
Assume all other status bits are the same. */
|
Assume all other status bits are the same. */
|
||||||
int host_to_target_waitstatus(int status)
|
int host_to_target_waitstatus(int status)
|
||||||
@ -5724,6 +5859,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
|
if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
|
||||||
goto efault;
|
goto efault;
|
||||||
ret = get_errno(read(arg1, p, arg3));
|
ret = get_errno(read(arg1, p, arg3));
|
||||||
|
if (ret >= 0 &&
|
||||||
|
fd_trans_host_to_target(arg1)) {
|
||||||
|
ret = fd_trans_host_to_target(arg1)(p, ret);
|
||||||
|
}
|
||||||
unlock_user(p, arg2, ret);
|
unlock_user(p, arg2, ret);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -5740,6 +5879,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
ret = get_errno(do_openat(cpu_env, AT_FDCWD, p,
|
ret = get_errno(do_openat(cpu_env, AT_FDCWD, p,
|
||||||
target_to_host_bitmask(arg2, fcntl_flags_tbl),
|
target_to_host_bitmask(arg2, fcntl_flags_tbl),
|
||||||
arg3));
|
arg3));
|
||||||
|
fd_trans_unregister(ret);
|
||||||
unlock_user(p, arg1, 0);
|
unlock_user(p, arg1, 0);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
@ -5749,6 +5889,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
ret = get_errno(do_openat(cpu_env, arg1, p,
|
ret = get_errno(do_openat(cpu_env, arg1, p,
|
||||||
target_to_host_bitmask(arg3, fcntl_flags_tbl),
|
target_to_host_bitmask(arg3, fcntl_flags_tbl),
|
||||||
arg4));
|
arg4));
|
||||||
|
fd_trans_unregister(ret);
|
||||||
unlock_user(p, arg2, 0);
|
unlock_user(p, arg2, 0);
|
||||||
break;
|
break;
|
||||||
#if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE)
|
#if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE)
|
||||||
@ -5759,9 +5900,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
#if defined(TARGET_NR_open_by_handle_at) && defined(CONFIG_OPEN_BY_HANDLE)
|
#if defined(TARGET_NR_open_by_handle_at) && defined(CONFIG_OPEN_BY_HANDLE)
|
||||||
case TARGET_NR_open_by_handle_at:
|
case TARGET_NR_open_by_handle_at:
|
||||||
ret = do_open_by_handle_at(arg1, arg2, arg3);
|
ret = do_open_by_handle_at(arg1, arg2, arg3);
|
||||||
|
fd_trans_unregister(ret);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case TARGET_NR_close:
|
case TARGET_NR_close:
|
||||||
|
fd_trans_unregister(arg1);
|
||||||
ret = get_errno(close(arg1));
|
ret = get_errno(close(arg1));
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_brk:
|
case TARGET_NR_brk:
|
||||||
@ -5803,6 +5946,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
if (!(p = lock_user_string(arg1)))
|
if (!(p = lock_user_string(arg1)))
|
||||||
goto efault;
|
goto efault;
|
||||||
ret = get_errno(creat(p, arg2));
|
ret = get_errno(creat(p, arg2));
|
||||||
|
fd_trans_unregister(ret);
|
||||||
unlock_user(p, arg1, 0);
|
unlock_user(p, arg1, 0);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
@ -6250,6 +6394,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
#endif
|
#endif
|
||||||
case TARGET_NR_dup:
|
case TARGET_NR_dup:
|
||||||
ret = get_errno(dup(arg1));
|
ret = get_errno(dup(arg1));
|
||||||
|
if (ret >= 0) {
|
||||||
|
fd_trans_dup(arg1, ret);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
#ifdef TARGET_NR_pipe
|
#ifdef TARGET_NR_pipe
|
||||||
case TARGET_NR_pipe:
|
case TARGET_NR_pipe:
|
||||||
@ -6347,11 +6494,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
#ifdef TARGET_NR_dup2
|
#ifdef TARGET_NR_dup2
|
||||||
case TARGET_NR_dup2:
|
case TARGET_NR_dup2:
|
||||||
ret = get_errno(dup2(arg1, arg2));
|
ret = get_errno(dup2(arg1, arg2));
|
||||||
|
if (ret >= 0) {
|
||||||
|
fd_trans_dup(arg1, arg2);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#if defined(CONFIG_DUP3) && defined(TARGET_NR_dup3)
|
#if defined(CONFIG_DUP3) && defined(TARGET_NR_dup3)
|
||||||
case TARGET_NR_dup3:
|
case TARGET_NR_dup3:
|
||||||
ret = get_errno(dup3(arg1, arg2, arg3));
|
ret = get_errno(dup3(arg1, arg2, arg3));
|
||||||
|
if (ret >= 0) {
|
||||||
|
fd_trans_dup(arg1, arg2);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef TARGET_NR_getppid /* not on alpha */
|
#ifdef TARGET_NR_getppid /* not on alpha */
|
||||||
@ -7347,6 +7500,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
#ifdef TARGET_NR_socket
|
#ifdef TARGET_NR_socket
|
||||||
case TARGET_NR_socket:
|
case TARGET_NR_socket:
|
||||||
ret = do_socket(arg1, arg2, arg3);
|
ret = do_socket(arg1, arg2, arg3);
|
||||||
|
fd_trans_unregister(ret);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef TARGET_NR_socketpair
|
#ifdef TARGET_NR_socketpair
|
||||||
@ -9600,6 +9754,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
#if defined(TARGET_NR_eventfd)
|
#if defined(TARGET_NR_eventfd)
|
||||||
case TARGET_NR_eventfd:
|
case TARGET_NR_eventfd:
|
||||||
ret = get_errno(eventfd(arg1, 0));
|
ret = get_errno(eventfd(arg1, 0));
|
||||||
|
fd_trans_unregister(ret);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#if defined(TARGET_NR_eventfd2)
|
#if defined(TARGET_NR_eventfd2)
|
||||||
@ -9613,6 +9768,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
host_flags |= O_CLOEXEC;
|
host_flags |= O_CLOEXEC;
|
||||||
}
|
}
|
||||||
ret = get_errno(eventfd(arg1, host_flags));
|
ret = get_errno(eventfd(arg1, host_flags));
|
||||||
|
fd_trans_unregister(ret);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -9655,6 +9811,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(TARGET_NR_signalfd4)
|
||||||
|
case TARGET_NR_signalfd4:
|
||||||
|
ret = do_signalfd4(arg1, arg2, arg4);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#if defined(TARGET_NR_signalfd)
|
||||||
|
case TARGET_NR_signalfd:
|
||||||
|
ret = do_signalfd4(arg1, arg2, 0);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
#if defined(CONFIG_EPOLL)
|
#if defined(CONFIG_EPOLL)
|
||||||
#if defined(TARGET_NR_epoll_create)
|
#if defined(TARGET_NR_epoll_create)
|
||||||
case TARGET_NR_epoll_create:
|
case TARGET_NR_epoll_create:
|
||||||
@ -9926,6 +10092,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
|||||||
timer_t htimer = g_posix_timers[timerid];
|
timer_t htimer = g_posix_timers[timerid];
|
||||||
ret = get_errno(timer_getoverrun(htimer));
|
ret = get_errno(timer_getoverrun(htimer));
|
||||||
}
|
}
|
||||||
|
fd_trans_unregister(ret);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user