Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/fhandler/signalfd.cc')
-rw-r--r--winsup/cygwin/fhandler/signalfd.cc160
1 files changed, 160 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler/signalfd.cc b/winsup/cygwin/fhandler/signalfd.cc
new file mode 100644
index 000000000..bdd8bc93e
--- /dev/null
+++ b/winsup/cygwin/fhandler/signalfd.cc
@@ -0,0 +1,160 @@
+/* fhandler_signalfd.cc: fhandler for signalfd
+
+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 <cygwin/signal.h>
+#include <sys/signalfd.h>
+
+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 ());
+ set_flags (O_RDWR | O_BINARY);
+ }
+ return 0;
+}
+
+int
+fhandler_signalfd::fstat (struct stat *buf)
+{
+ int ret = fhandler_base::fstat (buf);
+ if (!ret)
+ {
+ buf->st_mode = S_IRUSR | S_IWUSR;
+ 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
+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 we already read a signal so the buffer isn't empty, just
+ return success. */
+ if (curlen > 0)
+ break;
+ len = -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;
+}
+
+ssize_t
+fhandler_signalfd::write (const void *, size_t)
+{
+ set_errno (EINVAL);
+ return -1;
+}
+
+/* Called from select */
+int
+fhandler_signalfd::poll ()
+{
+ sigset_t outset = sig_send (myself, __SIGPENDING, &_my_tls);
+ if (outset == SIG_BAD_MASK)
+ return -1;
+ if ((outset & sigset) != 0)
+ return 0;
+ return -1;
+}