/* 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 #include 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. */ #define cnew(name, ...) \ ({ \ void* ptr = (void*) ccalloc (HEAP_FHANDLER, 1, sizeof (name)); \ ptr ? new (ptr) name (__VA_ARGS__) : NULL; \ }) int fhandler_timerfd::timerfd (clockid_t clock_id, int flags) { timerfd_tracker *tfd = cnew (timerfd_tracker); if (!tfd) { set_errno (ENOMEM); return -1; } 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->increment_instances (); 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; if (close_on_exec ()) tfd->decrement_instances (); else tfd->fixup_after_exec (); } __except (EFAULT) {} __endtry } fhandler_timerfd::~fhandler_timerfd () { __try { timerfd_tracker *tfd = (timerfd_tracker *) timerid; timerfd_tracker::dtor (tfd); } __except (EFAULT) {} __endtry } int fhandler_timerfd::close () { int ret = -1; __try { timerfd_tracker *tfd = (timerfd_tracker *) timerid; tfd->close (); 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); }