161 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			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 __reg2
 | 
						|
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 __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 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 __stdcall
 | 
						|
fhandler_signalfd::write (const void *, size_t)
 | 
						|
{
 | 
						|
  set_errno (EINVAL);
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
/* Called from select */
 | 
						|
int
 | 
						|
fhandler_signalfd::poll ()
 | 
						|
{
 | 
						|
  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;
 | 
						|
}
 |