From 9d13a2995cb4b6fd26cd7b7a2c478ad85115e055 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Sun, 13 Jan 2019 23:13:33 +0100 Subject: [PATCH] Cygwin: signal: implement signalfd First cut of a signalfd implementation. Still TODO: Non-polling select. This should mostly work as on Linux except for missing support for some members of struct signalfd_siginfo, namely ssi_fd, ssi_band (both SIGIO/SIGPOLL, not fully implemented) and ssi_trapno (HW exception, required HW support). Signed-off-by: Corinna Vinschen --- winsup/cygwin/Makefile.in | 1 + winsup/cygwin/common.din | 1 + winsup/cygwin/devices.cc | 3 + winsup/cygwin/devices.h | 4 + winsup/cygwin/devices.in | 3 + winsup/cygwin/dtable.cc | 3 + winsup/cygwin/fhandler.cc | 3 + winsup/cygwin/fhandler.h | 40 +++++++ winsup/cygwin/fhandler_signalfd.cc | 153 +++++++++++++++++++++++++ winsup/cygwin/include/cygwin/version.h | 3 +- winsup/cygwin/include/sys/signalfd.h | 54 +++++++++ winsup/cygwin/release/2.12.0 | 2 +- winsup/cygwin/select.cc | 65 +++++++++++ winsup/cygwin/signal.cc | 62 +++++++++- winsup/cygwin/sigproc.h | 1 + winsup/doc/new-features.xml | 2 +- winsup/doc/posix.xml | 1 + 17 files changed, 397 insertions(+), 4 deletions(-) create mode 100644 winsup/cygwin/fhandler_signalfd.cc create mode 100644 winsup/cygwin/include/sys/signalfd.h diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in index e4ce31fda..6147e7c9f 100644 --- a/winsup/cygwin/Makefile.in +++ b/winsup/cygwin/Makefile.in @@ -298,6 +298,7 @@ DLL_OFILES:= \ fhandler_raw.o \ fhandler_registry.o \ fhandler_serial.o \ + fhandler_signalfd.o \ fhandler_socket.o \ fhandler_socket_inet.o \ fhandler_socket_local.o \ diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din index a6363e1ba..b7f39f9c0 100644 --- a/winsup/cygwin/common.din +++ b/winsup/cygwin/common.din @@ -1333,6 +1333,7 @@ siginterrupt SIGFE sigismember SIGFE siglongjmp NOSIGFE signal SIGFE +signalfd SIGFE significand NOSIGFE significandf NOSIGFE sigpause SIGFE diff --git a/winsup/cygwin/devices.cc b/winsup/cygwin/devices.cc index a830b3202..31fd64fa2 100644 --- a/winsup/cygwin/devices.cc +++ b/winsup/cygwin/devices.cc @@ -120,6 +120,9 @@ const _device dev_piper_storage = const _device dev_pipew_storage = {"", {FH_PIPEW}, "", exists_internal}; +const _device dev_signalfd_storage = + {"", {FH_SIGNALFD}, "", exists_internal}; + const _device dev_socket_storage = {"", {FH_SOCKET}, "", exists_internal}; diff --git a/winsup/cygwin/devices.h b/winsup/cygwin/devices.h index 0c1bf2631..065f77e0e 100644 --- a/winsup/cygwin/devices.h +++ b/winsup/cygwin/devices.h @@ -72,6 +72,8 @@ enum fh_devices FH_DEV = FHDEV (DEV_VIRTFS_MAJOR, 193), FH_CYGDRIVE= FHDEV (DEV_VIRTFS_MAJOR, 192), + FH_SIGNALFD= FHDEV (DEV_VIRTFS_MAJOR, 13), + DEV_FLOPPY_MAJOR = 2, FH_FLOPPY = FHDEV (DEV_FLOPPY_MAJOR, 0), @@ -400,6 +402,8 @@ extern const _device dev_af_local_storage; extern const _device dev_af_unix_storage; #define af_unix_dev ((device *) &dev_af_unix_storage) +extern const _device dev_signalfd_storage; +#define signalfd_dev ((device *) &dev_signalfd_storage) extern const _device dev_piper_storage; #define piper_dev ((device *) &dev_piper_storage) extern const _device dev_pipew_storage; diff --git a/winsup/cygwin/devices.in b/winsup/cygwin/devices.in index 9ad87389e..79a7fe726 100644 --- a/winsup/cygwin/devices.in +++ b/winsup/cygwin/devices.in @@ -116,6 +116,9 @@ const _device dev_piper_storage = const _device dev_pipew_storage = {"", {FH_PIPEW}, "", exists_internal}; +const _device dev_signalfd_storage = + {"", {FH_SIGNALFD}, "", exists_internal}; + const _device dev_socket_storage = {"", {FH_SOCKET}, "", exists_internal}; diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc index 780deb524..c8aecfa1a 100644 --- a/winsup/cygwin/dtable.cc +++ b/winsup/cygwin/dtable.cc @@ -575,6 +575,9 @@ fh_alloc (path_conv& pc) case FH_CYGDRIVE: fh = cnew (fhandler_cygdrive); break; + case FH_SIGNALFD: + fh = cnew (fhandler_signalfd); + break; case FH_TTY: if (!pc.isopen ()) { diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc index 9643373b0..2c1fcb7db 100644 --- a/winsup/cygwin/fhandler.cc +++ b/winsup/cygwin/fhandler.cc @@ -1345,6 +1345,9 @@ fhandler_base::fstat (struct stat *buf) case FH_PIPER: buf->st_mode = S_IFIFO | S_IRUSR; break; + case FH_SIGNALFD: + buf->st_mode = S_IRUSR | S_IWUSR; + break; default: buf->st_mode = S_IFCHR | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH; break; diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index ec111de36..a908964e0 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -420,6 +420,7 @@ public: virtual class fhandler_socket *is_socket () { return NULL; } virtual class fhandler_socket_wsock *is_wsock_socket () { return NULL; } virtual class fhandler_console *is_console () { return 0; } + virtual class fhandler_signalfd *is_signalfd () { return NULL; } virtual int is_windows () {return 0; } virtual void __reg3 raw_read (void *ptr, size_t& ulen); @@ -2633,6 +2634,44 @@ class fhandler_procnet: public fhandler_proc } }; +class fhandler_signalfd : public fhandler_base +{ + sigset_t sigset; + + public: + fhandler_signalfd (); + fhandler_signalfd (void *) {} + + fhandler_signalfd *is_signalfd () { return this; } + + char *get_proc_fd_name (char *buf); + + int signalfd (const sigset_t *mask, int flags); + int __reg2 fstat (struct stat *buf); + void __reg3 read (void *ptr, size_t& len); + + int poll (); + + select_record *select_read (select_stuff *); + select_record *select_write (select_stuff *); + select_record *select_except (select_stuff *); + + void copyto (fhandler_base *x) + { + x->pc.free_strings (); + *reinterpret_cast (x) = *this; + x->reset (this); + } + + fhandler_signalfd *clone (cygheap_types malloc_type = HEAP_FHANDLER) + { + void *ptr = (void *) ccalloc (malloc_type, 1, sizeof (fhandler_signalfd)); + fhandler_signalfd *fh = new (ptr) fhandler_signalfd (ptr); + copyto (fh); + return fh; + } +}; + struct fhandler_nodevice: public fhandler_base { fhandler_nodevice (); @@ -2672,6 +2711,7 @@ typedef union char __pty_master[sizeof (fhandler_pty_master)]; char __registry[sizeof (fhandler_registry)]; char __serial[sizeof (fhandler_serial)]; + char __signalfd[sizeof (fhandler_signalfd)]; char __socket_inet[sizeof (fhandler_socket_inet)]; char __socket_local[sizeof (fhandler_socket_local)]; #ifdef __WITH_AF_UNIX diff --git a/winsup/cygwin/fhandler_signalfd.cc b/winsup/cygwin/fhandler_signalfd.cc new file mode 100644 index 000000000..ec80948fb --- /dev/null +++ b/winsup/cygwin/fhandler_signalfd.cc @@ -0,0 +1,153 @@ +/* fhandler_signalfd.cc: fhandler for /proc//fd/ operations + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#include "winsup.h" +#include "path.h" +#include "fhandler.h" +#include "pinfo.h" +#include "dtable.h" +#include "cygheap.h" +#include "sigproc.h" +#include +#include + +fhandler_signalfd::fhandler_signalfd () : + fhandler_base (), + sigset (0) +{ +} + +char * +fhandler_signalfd::get_proc_fd_name (char *buf) +{ + return strcpy (buf, "anon_inode:[signalfd]"); +} + +int +fhandler_signalfd::signalfd (const sigset_t *mask, int flags) +{ + __try + { + sigset = *mask & ~(SIGKILL | SIGSTOP); + } + __except (EINVAL) + { + return -1; + } + __endtry + if (flags & SFD_NONBLOCK) + set_nonblocking (true); + if (flags & SFD_CLOEXEC) + set_close_on_exec (true); + if (get_unique_id () == 0) + { + nohandle (true); + set_unique_id (); + set_ino (get_unique_id ()); + } + return 0; +} + +int __reg2 +fhandler_signalfd::fstat (struct stat *buf) +{ + int ret = fhandler_base::fstat (buf); + if (!ret) + { + buf->st_dev = FH_SIGNALFD; + buf->st_ino = get_unique_id (); + } + return ret; +} + +static inline void +copy_siginfo_to_signalfd (struct signalfd_siginfo *sfd, + const siginfo_t * const si) +{ + sfd->ssi_signo = si->si_signo; + sfd->ssi_errno = si->si_errno; + sfd->ssi_code = si->si_code; + sfd->ssi_pid = si->si_pid; + sfd->ssi_uid = si->si_uid; + sfd->ssi_fd = -1; + sfd->ssi_tid = si->si_tid; + sfd->ssi_band = 0; + sfd->ssi_overrun = si->si_overrun; + sfd->ssi_trapno = 0; + sfd->ssi_status = si->si_status; + sfd->ssi_int = si->si_value.sival_int; + sfd->ssi_ptr = (uint64_t) si->si_value.sival_ptr; + sfd->ssi_utime = si->si_utime; + sfd->ssi_stime = si->si_stime; + sfd->ssi_addr = (uint64_t) si->si_addr; +} + +void __reg3 +fhandler_signalfd::read (void *ptr, size_t& len) +{ + const LARGE_INTEGER poll = { QuadPart : 0 }; + siginfo_t si; + int ret, old_errno; + size_t curlen = 0; + signalfd_siginfo *sfd_ptr = (signalfd_siginfo *) ptr; + + if (len < sizeof (struct signalfd_siginfo)) + { + set_errno (EINVAL); + len = (size_t) -1; + return; + } + old_errno = get_errno (); + do + { + /* Even when read is blocking, only one pending signal is actually + required to return. Subsequently use sigtimedwait to just poll + if some more signal is available. */ + ret = sigwait_common (&sigset, &si, (is_nonblocking () || curlen) + ? (PLARGE_INTEGER) &poll : NULL); + if (ret == -1) + { + if (curlen == 0) + { + if (get_errno () == EINTR && curlen == 0) + continue; + set_errno (old_errno); + } + len = curlen ?: (size_t) -1; + return; + } + __try + { + copy_siginfo_to_signalfd (sfd_ptr, &si); + } + __except (EFAULT) + { + len = (size_t) -1; + return; + } + __endtry + sfd_ptr++; + curlen += sizeof (*sfd_ptr); + } + while ((len - curlen >= sizeof (struct signalfd_siginfo))); + set_errno (old_errno); + len = curlen; + return; +} + +int +fhandler_signalfd::poll () +{ + Sleep (1L); /* BAD HACK, FIXME, need a non-polling technique. */ + sigset_t outset = (sigset_t) sig_send (myself, __SIGPENDING, &_my_tls); + if (outset == SIG_BAD_MASK) + return -1; + if ((outset & sigset) != 0) + return 0; + return -1; +} diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 1bcec331f..ec5f55fa9 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -502,12 +502,13 @@ details. */ 330: Add CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_COARSE, CLOCK_BOOTTIME. 331: Add timer_getoverrun, DELAYTIMER_MAX. + 332: Add signalfd. Note that we forgot to bump the api for ualarm, strtoll, strtoull, sigaltstack, sethostname. */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 331 +#define CYGWIN_VERSION_API_MINOR 332 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible changes are made to the shared diff --git a/winsup/cygwin/include/sys/signalfd.h b/winsup/cygwin/include/sys/signalfd.h new file mode 100644 index 000000000..f4e5bd5c1 --- /dev/null +++ b/winsup/cygwin/include/sys/signalfd.h @@ -0,0 +1,54 @@ +/* sys/signalfd.h: define signalfd(2) and struct signalfd_siginfo + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifndef _SYS_SIGNALFD_H +#define _SYS_SIGNALFD_H + +#include +#include + +enum +{ + SFD_CLOEXEC = O_CLOEXEC, + SFD_NONBLOCK = O_NONBLOCK +}; +#define SFD_CLOEXEC SFD_CLOEXEC +#define SFD_NONBLOCK SFD_NONBLOCK + +struct signalfd_siginfo +{ + uint32_t ssi_signo; + int32_t ssi_errno; + int32_t ssi_code; + uint32_t ssi_pid; + uint32_t ssi_uid; + int32_t ssi_fd; + uint32_t ssi_tid; + uint32_t ssi_band; + uint32_t ssi_overrun; + uint32_t ssi_trapno; + int32_t ssi_status; + int32_t ssi_int; + uint64_t ssi_ptr; + uint64_t ssi_utime; + uint64_t ssi_stime; + uint64_t ssi_addr; + uint8_t pad[48]; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +extern int signalfd (int, const sigset_t *, int); + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_SIGNALFD_H */ diff --git a/winsup/cygwin/release/2.12.0 b/winsup/cygwin/release/2.12.0 index dbda7886e..299ea6d3d 100644 --- a/winsup/cygwin/release/2.12.0 +++ b/winsup/cygwin/release/2.12.0 @@ -27,7 +27,7 @@ What's new: - Support overrun counter for posix timers (via timer_getoverrun() or siginfo_t::si_overrun). -- New API: timer_getoverrun. +- New API: signalfd, timer_getoverrun. What changed: diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index 6ce679acf..3927b9885 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -1731,3 +1731,68 @@ fhandler_windows::select_except (select_stuff *ss) s->windows_handle = true; return s; } + +static int +peek_signalfd (select_record *me, bool) +{ + if (((fhandler_signalfd *) me->fh)->poll () == 0) + { + select_printf ("signalfd %d ready", me->fd); + return 1; + } + + select_printf ("signalfd %d not ready", me->fd); + return 0; +} + +static int +verify_signalfd (select_record *me, fd_set *rfds, fd_set *wfds, + fd_set *efds) +{ + return peek_signalfd (me, true); +} + +select_record * +fhandler_signalfd::select_read (select_stuff *ss) +{ + select_record *s = ss->start.next; + if (!s->startup) + { + s->startup = no_startup; + } + s->verify = verify_signalfd; + s->peek = peek_signalfd; + s->read_selected = true; + s->read_ready = true; + return s; +} + +select_record * +fhandler_signalfd::select_write (select_stuff *ss) +{ + select_record *s = ss->start.next; + if (!s->startup) + { + s->startup = no_startup; + s->verify = no_verify; + } + s->peek = NULL; + s->write_selected = false; + s->write_ready = false; + return s; +} + +select_record * +fhandler_signalfd::select_except (select_stuff *ss) +{ + select_record *s = ss->start.next; + if (!s->startup) + { + s->startup = no_startup; + s->verify = no_verify; + } + s->peek = NULL; + s->except_selected = false; + s->except_ready = false; + return s; +} diff --git a/winsup/cygwin/signal.cc b/winsup/cygwin/signal.cc index c5e9d017e..5dee40229 100644 --- a/winsup/cygwin/signal.cc +++ b/winsup/cygwin/signal.cc @@ -12,6 +12,7 @@ details. */ #include "winsup.h" #include #include +#include #include "pinfo.h" #include "sigproc.h" #include "cygtls.h" @@ -592,7 +593,7 @@ siginterrupt (int sig, int flag) return res; } -static inline int +int sigwait_common (const sigset_t *set, siginfo_t *info, PLARGE_INTEGER waittime) { int res = -1; @@ -781,3 +782,62 @@ sigaltstack (const stack_t *ss, stack_t *oss) __endtry return 0; } + +extern "C" int +signalfd (int fd_in, const sigset_t *mask, int flags) +{ + int ret = -1; + fhandler_signalfd *fh; + + debug_printf ("signalfd (%d, %p, %y)", fd_in, mask, flags); + + if ((flags & ~(SFD_NONBLOCK | SFD_CLOEXEC)) != 0) + { + set_errno (EINVAL); + goto done; + } + + if (fd_in != -1) + { + /* Change signal mask. */ + cygheap_fdget fd (fd_in); + + if (fd < 0) + goto done; + fh = fd->is_signalfd (); + if (!fh) + { + set_errno (EINVAL); + goto done; + } + __try + { + if (fh->signalfd (mask, flags) == 0) + ret = fd_in; + } + __except (EINVAL) {} + __endtry + } + else + { + /* Create new signalfd descriptor. */ + cygheap_fdnew fd; + + if (fd < 0) + goto done; + fh = (fhandler_signalfd *) build_fh_dev (*signalfd_dev); + if (fh && fh->signalfd (mask, flags) == 0) + { + fd = fh; + if (fd <= 2) + set_std_handle (fd); + ret = fd; + } + else + delete fh; + } + +done: + syscall_printf ("%R = signalfd (%d, %p, %y)", ret, fd_in, mask, flags); + return ret; +} diff --git a/winsup/cygwin/sigproc.h b/winsup/cygwin/sigproc.h index f2cd1132b..9beb31dcf 100644 --- a/winsup/cygwin/sigproc.h +++ b/winsup/cygwin/sigproc.h @@ -79,6 +79,7 @@ void __stdcall sigalloc (); int kill_pgrp (pid_t, siginfo_t&); void __reg1 exit_thread (DWORD) __attribute__ ((noreturn)); void __reg1 setup_signal_exit (int); +int sigwait_common (const sigset_t *, siginfo_t *, PLARGE_INTEGER); class no_thread_exit_protect { diff --git a/winsup/doc/new-features.xml b/winsup/doc/new-features.xml index 2e1645fbe..17071309e 100644 --- a/winsup/doc/new-features.xml +++ b/winsup/doc/new-features.xml @@ -51,7 +51,7 @@ siginfo_t::si_overrun). -New API: timer_getoverrun. +New API: signalfd, timer_getoverrun. diff --git a/winsup/doc/posix.xml b/winsup/doc/posix.xml index 021b5962c..365a8f0b5 100644 --- a/winsup/doc/posix.xml +++ b/winsup/doc/posix.xml @@ -1378,6 +1378,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008). scandirat sched_getcpu setxattr + signalfd sincos sincosf sincosl -- 2.43.5