339 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			339 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* fhandler_timerfd.cc: fhandler for timerfd, public timerfd API
 | |
| 
 | |
| 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 "timerfd.h"
 | |
| #include <sys/timerfd.h>
 | |
| #include <cygwin/signal.h>
 | |
| 
 | |
| fhandler_timerfd::fhandler_timerfd () :
 | |
|   fhandler_base ()
 | |
| {
 | |
| }
 | |
| 
 | |
| char *
 | |
| fhandler_timerfd::get_proc_fd_name (char *buf)
 | |
| {
 | |
|   return strcpy (buf, "anon_inode:[timerfd]");
 | |
| }
 | |
| 
 | |
| /* The timers connected to a descriptor are stored on the cygheap
 | |
|    together with their fhandler. */
 | |
| 
 | |
| int
 | |
| fhandler_timerfd::timerfd (clockid_t clock_id, int flags)
 | |
| {
 | |
|   timerfd_tracker *tfd = (timerfd_tracker *)
 | |
| 			 ccalloc (HEAP_FHANDLER, 1, sizeof (timerfd_tracker));
 | |
|   if (!tfd)
 | |
|     {
 | |
|       set_errno (ENOMEM);
 | |
|       return -1;
 | |
|     }
 | |
|   new (tfd) timerfd_tracker ();
 | |
|   int ret = tfd->create (clock_id);
 | |
|   if (ret < 0)
 | |
|     {
 | |
|       cfree (tfd);
 | |
|       set_errno (-ret);
 | |
|       return -1;
 | |
|     }
 | |
|   if (flags & TFD_NONBLOCK)
 | |
|     set_nonblocking (true);
 | |
|   if (flags & TFD_CLOEXEC)
 | |
|     set_close_on_exec (true);
 | |
|   nohandle (true);
 | |
|   set_unique_id ();
 | |
|   set_ino (get_unique_id ());
 | |
|   set_flags (O_RDWR | O_BINARY);
 | |
|   timerid = (timer_t) tfd;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_timerfd::settime (int flags, const struct itimerspec *new_value,
 | |
| 			   struct itimerspec *old_value)
 | |
| {
 | |
|   int ret = -1;
 | |
| 
 | |
|   __try
 | |
|     {
 | |
|       timerfd_tracker *tfd = (timerfd_tracker *) timerid;
 | |
|       ret = tfd->settime (flags, new_value, old_value);
 | |
|       if (ret < 0)
 | |
| 	{
 | |
| 	  set_errno (-ret);
 | |
| 	  ret = -1;
 | |
| 	}
 | |
|     }
 | |
|   __except (EFAULT) {}
 | |
|   __endtry
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_timerfd::gettime (struct itimerspec *ovalue)
 | |
| {
 | |
|   int ret = -1;
 | |
| 
 | |
|   __try
 | |
|     {
 | |
|       timerfd_tracker *tfd = (timerfd_tracker *) timerid;
 | |
|       ret = tfd->gettime (ovalue);
 | |
|       if (ret < 0)
 | |
| 	{
 | |
| 	  set_errno (-ret);
 | |
| 	  ret = -1;
 | |
| 	}
 | |
|     }
 | |
|   __except (EFAULT) {}
 | |
|   __endtry
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_timerfd::fstat (struct stat *buf)
 | |
| {
 | |
|   int ret = fhandler_base::fstat (buf);
 | |
|   if (!ret)
 | |
|     {
 | |
|       buf->st_mode = S_IRUSR | S_IWUSR;
 | |
|       buf->st_dev = FH_TIMERFD;
 | |
|       buf->st_ino = get_unique_id ();
 | |
|     }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_timerfd::read (void *ptr, size_t& len)
 | |
| {
 | |
|   if (len < sizeof (LONG64))
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       len = (size_t) -1;
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   __try
 | |
|     {
 | |
|       timerfd_tracker *tfd = (timerfd_tracker *) timerid;
 | |
|       LONG64 ret = tfd->wait (is_nonblocking ());
 | |
|       if (ret < 0)
 | |
| 	{
 | |
| 	  set_errno (-ret);
 | |
| 	  __leave;
 | |
| 	}
 | |
|       *(PLONG64) ptr = ret;
 | |
|       len = sizeof (LONG64);
 | |
|       return;
 | |
|     }
 | |
|   __except (EFAULT) {}
 | |
|   __endtry
 | |
|   len = (size_t) -1;
 | |
|   return;
 | |
| }
 | |
| 
 | |
| ssize_t
 | |
| fhandler_timerfd::write (const void *, size_t)
 | |
| {
 | |
|   set_errno (EINVAL);
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| HANDLE
 | |
| fhandler_timerfd::get_timerfd_handle ()
 | |
| {
 | |
|   __try
 | |
|     {
 | |
|       timerfd_tracker *tfd = (timerfd_tracker *) timerid;
 | |
|       return tfd->get_timerfd_handle ();
 | |
|     }
 | |
|   __except (EFAULT) {}
 | |
|   __endtry
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_timerfd::dup (fhandler_base *child, int flags)
 | |
| {
 | |
|   int ret = fhandler_base::dup (child, flags);
 | |
| 
 | |
|   if (!ret)
 | |
|     {
 | |
|       fhandler_timerfd *fhc = (fhandler_timerfd *) child;
 | |
|       __try
 | |
| 	{
 | |
| 	  timerfd_tracker *tfd = (timerfd_tracker *) fhc->timerid;
 | |
| 	  tfd->dup ();
 | |
| 	  ret = 0;
 | |
| 	}
 | |
|       __except (EFAULT) {}
 | |
|       __endtry
 | |
|     }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_timerfd::ioctl (unsigned int cmd, void *p)
 | |
| {
 | |
|   int ret = -1;
 | |
|   uint64_t exp_cnt;
 | |
| 
 | |
|   switch (cmd)
 | |
|     {
 | |
|     case TFD_IOC_SET_TICKS:
 | |
|       __try
 | |
| 	{
 | |
| 	  timerfd_tracker *tfd = (timerfd_tracker *) timerid;
 | |
| 
 | |
| 	  exp_cnt = *(uint64_t *) p;
 | |
| 	  ret = tfd->ioctl_set_ticks (exp_cnt);
 | |
| 	  if (ret < 0)
 | |
| 	    set_errno (-ret);
 | |
| 	}
 | |
|       __except (EFAULT) {}
 | |
|       __endtry
 | |
|       break;
 | |
|     default:
 | |
|       ret = fhandler_base::ioctl (cmd, p);
 | |
|       break;
 | |
|     }
 | |
|   syscall_printf ("%d = ioctl_timerfd(%x, %p)", ret, cmd, p);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_timerfd::fixup_after_fork (HANDLE)
 | |
| {
 | |
|   __try
 | |
|     {
 | |
|       timerfd_tracker *tfd = (timerfd_tracker *) timerid;
 | |
|       tfd->fixup_after_fork ();
 | |
|     }
 | |
|   __except (EFAULT) {}
 | |
|   __endtry
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_timerfd::fixup_after_exec ()
 | |
| {
 | |
|   __try
 | |
|     {
 | |
|       timerfd_tracker *tfd = (timerfd_tracker *) timerid;
 | |
|       tfd->init_fixup_after_fork_exec ();
 | |
|       if (close_on_exec ())
 | |
| 	timerfd_tracker::dtor (tfd);
 | |
|       else
 | |
| 	tfd->fixup_after_exec ();
 | |
|     }
 | |
|   __except (EFAULT) {}
 | |
|   __endtry
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_timerfd::close ()
 | |
| {
 | |
|   int ret = -1;
 | |
| 
 | |
|   __try
 | |
|     {
 | |
|       timerfd_tracker *tfd = (timerfd_tracker *) timerid;
 | |
|       timerfd_tracker::dtor (tfd);
 | |
|       ret = 0;
 | |
|     }
 | |
|   __except (EFAULT) {}
 | |
|   __endtry
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| extern "C" int
 | |
| timerfd_create (clockid_t clock_id, int flags)
 | |
| {
 | |
|   int ret = -1;
 | |
|   fhandler_timerfd *fh;
 | |
| 
 | |
|   debug_printf ("timerfd_create (%lu, %y)", clock_id, flags);
 | |
| 
 | |
|   if (clock_id != CLOCK_REALTIME
 | |
|       && clock_id != CLOCK_MONOTONIC
 | |
|       && clock_id != CLOCK_BOOTTIME)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       goto done;
 | |
|     }
 | |
|   if ((flags & ~(TFD_NONBLOCK | TFD_CLOEXEC)) != 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       goto done;
 | |
|     }
 | |
| 
 | |
|     {
 | |
|       /* Create new timerfd descriptor. */
 | |
|       cygheap_fdnew fd;
 | |
| 
 | |
|       if (fd < 0)
 | |
|         goto done;
 | |
|       fh = (fhandler_timerfd *) build_fh_dev (*timerfd_dev);
 | |
|       if (fh && fh->timerfd (clock_id, flags) == 0)
 | |
|         {
 | |
|           fd = fh;
 | |
|           if (fd <= 2)
 | |
|             set_std_handle (fd);
 | |
|           ret = fd;
 | |
|         }
 | |
|       else
 | |
|         delete fh;
 | |
|     }
 | |
| 
 | |
| done:
 | |
|   syscall_printf ("%R = timerfd_create (%lu, %y)", ret, clock_id, flags);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| extern "C" int
 | |
| timerfd_settime (int fd_in, int flags, const struct itimerspec *value,
 | |
| 		 struct itimerspec *ovalue)
 | |
| {
 | |
|   if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   cygheap_fdget fd (fd_in);
 | |
|   if (fd < 0)
 | |
|     return -1;
 | |
|   fhandler_timerfd *fh = fd->is_timerfd ();
 | |
|   if (!fh)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   return fh->settime (flags, value, ovalue);
 | |
| }
 | |
| 
 | |
| extern "C" int
 | |
| timerfd_gettime (int fd_in, struct itimerspec *ovalue)
 | |
| {
 | |
|   cygheap_fdget fd (fd_in);
 | |
|   if (fd < 0)
 | |
|     return -1;
 | |
|   fhandler_timerfd *fh = fd->is_timerfd ();
 | |
|   if (!fh)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   return fh->gettime (ovalue);
 | |
| }
 |