newlib-cygwin/winsup/cygwin/fhandler_signalfd.cc

161 lines
3.4 KiB
C++

/* 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;
}