Message ID | 1443790089-11735-1-git-send-email-laurent@vivier.eu |
---|---|
State | New |
Headers | show |
On perjantaina 2. lokakuuta 2015 15.48.09 EEST, Laurent Vivier wrote: > This patch introduces a system very similar to the one used in the kernel > to attach specific functions to a given file descriptor. Thanks, applied to linux-user > 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> > --- > v4: rebase on top of Riku's linux-user-for-upstream > add fd_trans_unregister() for open_by_handle_at. > v3: Combine struct definition and typedef > replace a = x ? y : z by if () { } > clear extra memory from g_realloc() > remove useless management of O_CLOEXEC > swap ssi_addr_lsb > fix host_flags checking > v2: Update commit message with example from man page > Use CamelCase for struct > Allocate entries in the fd array on demand > Clear fd entries on open(), close(),... > Swap ssi_errno > Try to manage dup() and O_CLOEXEC cases > Fix signalfd() parameters > merge signalfd() and signalfd4() > Change the API to only provide functions to process > data stream. > > TargetFdTrans has an unused field, target_to_host, for symmetry > and which could used later with netlink() functions. > > linux-user/syscall.c | 167 > +++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 167 insertions(+) > > diff --git a/linux-user/syscall.c b/linux-user/syscall.c > index 98b5766..f4121af 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,54 @@ static bitmask_transtbl fcntl_flags_tbl[] = { > { 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) > { > if (getcwd(buf, size) == NULL) { > @@ -5341,6 +5390,92 @@ static abi_long > do_open_by_handle_at(abi_long mount_fd, abi_long handle, > } > #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. > Assume all other status bits are the same. */ > int host_to_target_waitstatus(int status) > @@ -5725,6 +5860,10 @@ abi_long do_syscall(void *cpu_env, int > num, abi_long arg1, > if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) > goto efault; > 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); > } > break; > @@ -5741,6 +5880,7 @@ abi_long do_syscall(void *cpu_env, int > num, abi_long arg1, > ret = get_errno(do_openat(cpu_env, AT_FDCWD, p, > target_to_host_bitmask(arg2, > fcntl_flags_tbl), > arg3)); > + fd_trans_unregister(ret); > unlock_user(p, arg1, 0); > break; > #endif > @@ -5750,6 +5890,7 @@ abi_long do_syscall(void *cpu_env, int > num, abi_long arg1, > ret = get_errno(do_openat(cpu_env, arg1, p, > target_to_host_bitmask(arg3, > fcntl_flags_tbl), > arg4)); > + fd_trans_unregister(ret); > unlock_user(p, arg2, 0); > break; > #if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) > @@ -5760,9 +5901,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) > case TARGET_NR_open_by_handle_at: > ret = do_open_by_handle_at(arg1, arg2, arg3); > + fd_trans_unregister(ret); > break; > #endif > case TARGET_NR_close: > + fd_trans_unregister(arg1); > ret = get_errno(close(arg1)); > break; > case TARGET_NR_brk: > @@ -5804,6 +5947,7 @@ abi_long do_syscall(void *cpu_env, int > num, abi_long arg1, > if (!(p = lock_user_string(arg1))) > goto efault; > ret = get_errno(creat(p, arg2)); > + fd_trans_unregister(ret); > unlock_user(p, arg1, 0); > break; > #endif > @@ -6251,6 +6395,9 @@ abi_long do_syscall(void *cpu_env, int > num, abi_long arg1, > #endif > case TARGET_NR_dup: > ret = get_errno(dup(arg1)); > + if (ret >= 0) { > + fd_trans_dup(arg1, ret); > + } > break; > #ifdef TARGET_NR_pipe > case TARGET_NR_pipe: > @@ -6348,11 +6495,17 @@ abi_long do_syscall(void *cpu_env, int > num, abi_long arg1, > #ifdef TARGET_NR_dup2 > case TARGET_NR_dup2: > ret = get_errno(dup2(arg1, arg2)); > + if (ret >= 0) { > + fd_trans_dup(arg1, arg2); > + } > break; > #endif > #if defined(CONFIG_DUP3) && defined(TARGET_NR_dup3) > case TARGET_NR_dup3: > ret = get_errno(dup3(arg1, arg2, arg3)); > + if (ret >= 0) { > + fd_trans_dup(arg1, arg2); > + } > break; > #endif > #ifdef TARGET_NR_getppid /* not on alpha */ > @@ -7348,6 +7501,7 @@ abi_long do_syscall(void *cpu_env, int > num, abi_long arg1, > #ifdef TARGET_NR_socket > case TARGET_NR_socket: > ret = do_socket(arg1, arg2, arg3); > + fd_trans_unregister(ret); > break; > #endif > #ifdef TARGET_NR_socketpair > @@ -9601,6 +9755,7 @@ abi_long do_syscall(void *cpu_env, int > num, abi_long arg1, > #if defined(TARGET_NR_eventfd) > case TARGET_NR_eventfd: > ret = get_errno(eventfd(arg1, 0)); > + fd_trans_unregister(ret); > break; > #endif > #if defined(TARGET_NR_eventfd2) > @@ -9614,6 +9769,7 @@ abi_long do_syscall(void *cpu_env, int > num, abi_long arg1, > host_flags |= O_CLOEXEC; > } > ret = get_errno(eventfd(arg1, host_flags)); > + fd_trans_unregister(ret); > break; > } > #endif > @@ -9656,6 +9812,16 @@ abi_long do_syscall(void *cpu_env, int > num, abi_long arg1, > break; > #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(TARGET_NR_epoll_create) > case TARGET_NR_epoll_create: > @@ -9927,6 +10093,7 @@ abi_long do_syscall(void *cpu_env, int > num, abi_long arg1, > timer_t htimer = g_posix_timers[timerid]; > ret = get_errno(timer_getoverrun(htimer)); > } > + fd_trans_unregister(ret); > break; > } > #endif
Le 06/10/2015 10:16, Riku Voipio a écrit : > On perjantaina 2. lokakuuta 2015 15.48.09 EEST, Laurent Vivier wrote: >> This patch introduces a system very similar to the one used in the kernel >> to attach specific functions to a given file descriptor. > > Thanks, applied to linux-user As I don't find this patch in origin/master, I guess linux-user has missed the 2.5 soft feature freeze ? Laurent
Le 06/10/2015 10:16, Riku Voipio a écrit : > On perjantaina 2. lokakuuta 2015 15.48.09 EEST, Laurent Vivier wrote: >> This patch introduces a system very similar to the one used in the kernel >> to attach specific functions to a given file descriptor. > > Thanks, applied to linux-user When will linux-user branch be pulled to master ? Laurent
On 02/10/2015 14:48, Laurent Vivier wrote: > + target_fd_trans = g_realloc(target_fd_trans, > + target_fd_max * sizeof(TargetFdTrans)); This should be TargetFdTrans * (reported by Coverity). Even better you could use g_renew. It's harmless because sizeof(TargetFdTrans) > sizeof(TargetFdTrans *), but it should be fixed nevertheless. Paolo > + memset((void *)(target_fd_trans + oldmax), 0, > + (target_fd_max - oldmax) * sizeof(TargetFdTrans *));
Le 18/01/2016 11:30, Paolo Bonzini a écrit : > > > On 02/10/2015 14:48, Laurent Vivier wrote: >> + target_fd_trans = g_realloc(target_fd_trans, >> + target_fd_max * sizeof(TargetFdTrans)); > > This should be TargetFdTrans * (reported by Coverity). Even better you > could use g_renew. > > It's harmless because sizeof(TargetFdTrans) > sizeof(TargetFdTrans *), > but it should be fixed nevertheless. OK, thank you Paolo. I will fix that. Laurent
diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 98b5766..f4121af 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,54 @@ static bitmask_transtbl fcntl_flags_tbl[] = { { 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) { if (getcwd(buf, size) == NULL) { @@ -5341,6 +5390,92 @@ static abi_long do_open_by_handle_at(abi_long mount_fd, abi_long handle, } #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. Assume all other status bits are the same. */ int host_to_target_waitstatus(int status) @@ -5725,6 +5860,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) goto efault; 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); } break; @@ -5741,6 +5880,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(do_openat(cpu_env, AT_FDCWD, p, target_to_host_bitmask(arg2, fcntl_flags_tbl), arg3)); + fd_trans_unregister(ret); unlock_user(p, arg1, 0); break; #endif @@ -5750,6 +5890,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(do_openat(cpu_env, arg1, p, target_to_host_bitmask(arg3, fcntl_flags_tbl), arg4)); + fd_trans_unregister(ret); unlock_user(p, arg2, 0); break; #if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) @@ -5760,9 +5901,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) case TARGET_NR_open_by_handle_at: ret = do_open_by_handle_at(arg1, arg2, arg3); + fd_trans_unregister(ret); break; #endif case TARGET_NR_close: + fd_trans_unregister(arg1); ret = get_errno(close(arg1)); break; case TARGET_NR_brk: @@ -5804,6 +5947,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!(p = lock_user_string(arg1))) goto efault; ret = get_errno(creat(p, arg2)); + fd_trans_unregister(ret); unlock_user(p, arg1, 0); break; #endif @@ -6251,6 +6395,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif case TARGET_NR_dup: ret = get_errno(dup(arg1)); + if (ret >= 0) { + fd_trans_dup(arg1, ret); + } break; #ifdef TARGET_NR_pipe case TARGET_NR_pipe: @@ -6348,11 +6495,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_dup2 case TARGET_NR_dup2: ret = get_errno(dup2(arg1, arg2)); + if (ret >= 0) { + fd_trans_dup(arg1, arg2); + } break; #endif #if defined(CONFIG_DUP3) && defined(TARGET_NR_dup3) case TARGET_NR_dup3: ret = get_errno(dup3(arg1, arg2, arg3)); + if (ret >= 0) { + fd_trans_dup(arg1, arg2); + } break; #endif #ifdef TARGET_NR_getppid /* not on alpha */ @@ -7348,6 +7501,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_socket case TARGET_NR_socket: ret = do_socket(arg1, arg2, arg3); + fd_trans_unregister(ret); break; #endif #ifdef TARGET_NR_socketpair @@ -9601,6 +9755,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #if defined(TARGET_NR_eventfd) case TARGET_NR_eventfd: ret = get_errno(eventfd(arg1, 0)); + fd_trans_unregister(ret); break; #endif #if defined(TARGET_NR_eventfd2) @@ -9614,6 +9769,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, host_flags |= O_CLOEXEC; } ret = get_errno(eventfd(arg1, host_flags)); + fd_trans_unregister(ret); break; } #endif @@ -9656,6 +9812,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, break; #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(TARGET_NR_epoll_create) case TARGET_NR_epoll_create: @@ -9927,6 +10093,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, timer_t htimer = g_posix_timers[timerid]; ret = get_errno(timer_getoverrun(htimer)); } + fd_trans_unregister(ret); break; } #endif
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> --- v4: rebase on top of Riku's linux-user-for-upstream add fd_trans_unregister() for open_by_handle_at. v3: Combine struct definition and typedef replace a = x ? y : z by if () { } clear extra memory from g_realloc() remove useless management of O_CLOEXEC swap ssi_addr_lsb fix host_flags checking v2: Update commit message with example from man page Use CamelCase for struct Allocate entries in the fd array on demand Clear fd entries on open(), close(),... Swap ssi_errno Try to manage dup() and O_CLOEXEC cases Fix signalfd() parameters merge signalfd() and signalfd4() Change the API to only provide functions to process data stream. TargetFdTrans has an unused field, target_to_host, for symmetry and which could used later with netlink() functions. linux-user/syscall.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+)