338 lines
6.2 KiB
C++
338 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 __reg2
|
|
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 __reg3
|
|
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 __stdcall
|
|
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);
|
|
}
|