Cygwin: signal: implement signalfd

First cut of a signalfd implementation.

Still TODO: Non-polling select.

This should mostly work as on Linux except for missing support
for some members of struct signalfd_siginfo, namely ssi_fd,
ssi_band (both SIGIO/SIGPOLL, not fully implemented) and ssi_trapno
(HW exception, required HW support).

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2019-01-13 23:13:33 +01:00
parent 8ae26f96ae
commit 9d13a2995c
17 changed files with 397 additions and 4 deletions

View File

@ -298,6 +298,7 @@ DLL_OFILES:= \
fhandler_raw.o \
fhandler_registry.o \
fhandler_serial.o \
fhandler_signalfd.o \
fhandler_socket.o \
fhandler_socket_inet.o \
fhandler_socket_local.o \

View File

@ -1333,6 +1333,7 @@ siginterrupt SIGFE
sigismember SIGFE
siglongjmp NOSIGFE
signal SIGFE
signalfd SIGFE
significand NOSIGFE
significandf NOSIGFE
sigpause SIGFE

View File

@ -120,6 +120,9 @@ const _device dev_piper_storage =
const _device dev_pipew_storage =
{"", {FH_PIPEW}, "", exists_internal};
const _device dev_signalfd_storage =
{"", {FH_SIGNALFD}, "", exists_internal};
const _device dev_socket_storage =
{"", {FH_SOCKET}, "", exists_internal};

View File

@ -72,6 +72,8 @@ enum fh_devices
FH_DEV = FHDEV (DEV_VIRTFS_MAJOR, 193),
FH_CYGDRIVE= FHDEV (DEV_VIRTFS_MAJOR, 192),
FH_SIGNALFD= FHDEV (DEV_VIRTFS_MAJOR, 13),
DEV_FLOPPY_MAJOR = 2,
FH_FLOPPY = FHDEV (DEV_FLOPPY_MAJOR, 0),
@ -400,6 +402,8 @@ extern const _device dev_af_local_storage;
extern const _device dev_af_unix_storage;
#define af_unix_dev ((device *) &dev_af_unix_storage)
extern const _device dev_signalfd_storage;
#define signalfd_dev ((device *) &dev_signalfd_storage)
extern const _device dev_piper_storage;
#define piper_dev ((device *) &dev_piper_storage)
extern const _device dev_pipew_storage;

View File

@ -116,6 +116,9 @@ const _device dev_piper_storage =
const _device dev_pipew_storage =
{"", {FH_PIPEW}, "", exists_internal};
const _device dev_signalfd_storage =
{"", {FH_SIGNALFD}, "", exists_internal};
const _device dev_socket_storage =
{"", {FH_SOCKET}, "", exists_internal};

View File

@ -575,6 +575,9 @@ fh_alloc (path_conv& pc)
case FH_CYGDRIVE:
fh = cnew (fhandler_cygdrive);
break;
case FH_SIGNALFD:
fh = cnew (fhandler_signalfd);
break;
case FH_TTY:
if (!pc.isopen ())
{

View File

@ -1345,6 +1345,9 @@ fhandler_base::fstat (struct stat *buf)
case FH_PIPER:
buf->st_mode = S_IFIFO | S_IRUSR;
break;
case FH_SIGNALFD:
buf->st_mode = S_IRUSR | S_IWUSR;
break;
default:
buf->st_mode = S_IFCHR | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
break;

View File

@ -420,6 +420,7 @@ public:
virtual class fhandler_socket *is_socket () { return NULL; }
virtual class fhandler_socket_wsock *is_wsock_socket () { return NULL; }
virtual class fhandler_console *is_console () { return 0; }
virtual class fhandler_signalfd *is_signalfd () { return NULL; }
virtual int is_windows () {return 0; }
virtual void __reg3 raw_read (void *ptr, size_t& ulen);
@ -2633,6 +2634,44 @@ class fhandler_procnet: public fhandler_proc
}
};
class fhandler_signalfd : public fhandler_base
{
sigset_t sigset;
public:
fhandler_signalfd ();
fhandler_signalfd (void *) {}
fhandler_signalfd *is_signalfd () { return this; }
char *get_proc_fd_name (char *buf);
int signalfd (const sigset_t *mask, int flags);
int __reg2 fstat (struct stat *buf);
void __reg3 read (void *ptr, size_t& len);
int poll ();
select_record *select_read (select_stuff *);
select_record *select_write (select_stuff *);
select_record *select_except (select_stuff *);
void copyto (fhandler_base *x)
{
x->pc.free_strings ();
*reinterpret_cast<fhandler_signalfd *> (x) = *this;
x->reset (this);
}
fhandler_signalfd *clone (cygheap_types malloc_type = HEAP_FHANDLER)
{
void *ptr = (void *) ccalloc (malloc_type, 1, sizeof (fhandler_signalfd));
fhandler_signalfd *fh = new (ptr) fhandler_signalfd (ptr);
copyto (fh);
return fh;
}
};
struct fhandler_nodevice: public fhandler_base
{
fhandler_nodevice ();
@ -2672,6 +2711,7 @@ typedef union
char __pty_master[sizeof (fhandler_pty_master)];
char __registry[sizeof (fhandler_registry)];
char __serial[sizeof (fhandler_serial)];
char __signalfd[sizeof (fhandler_signalfd)];
char __socket_inet[sizeof (fhandler_socket_inet)];
char __socket_local[sizeof (fhandler_socket_local)];
#ifdef __WITH_AF_UNIX

View File

@ -0,0 +1,153 @@
/* fhandler_signalfd.cc: fhandler for /proc/<pid>/fd/<desc> operations
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 ());
}
return 0;
}
int __reg2
fhandler_signalfd::fstat (struct stat *buf)
{
int ret = fhandler_base::fstat (buf);
if (!ret)
{
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 (curlen == 0)
{
if (get_errno () == EINTR && curlen == 0)
continue;
set_errno (old_errno);
}
len = curlen ?: (size_t) -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;
}
int
fhandler_signalfd::poll ()
{
Sleep (1L); /* BAD HACK, FIXME, need a non-polling technique. */
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;
}

View File

@ -502,12 +502,13 @@ details. */
330: Add CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_RAW, CLOCK_MONOTONIC_COARSE,
CLOCK_BOOTTIME.
331: Add timer_getoverrun, DELAYTIMER_MAX.
332: Add signalfd.
Note that we forgot to bump the api for ualarm, strtoll, strtoull,
sigaltstack, sethostname. */
#define CYGWIN_VERSION_API_MAJOR 0
#define CYGWIN_VERSION_API_MINOR 331
#define CYGWIN_VERSION_API_MINOR 332
/* There is also a compatibity version number associated with the shared memory
regions. It is incremented when incompatible changes are made to the shared

View File

@ -0,0 +1,54 @@
/* sys/signalfd.h: define signalfd(2) and struct signalfd_siginfo
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. */
#ifndef _SYS_SIGNALFD_H
#define _SYS_SIGNALFD_H
#include <stdint.h>
#include <sys/_default_fcntl.h>
enum
{
SFD_CLOEXEC = O_CLOEXEC,
SFD_NONBLOCK = O_NONBLOCK
};
#define SFD_CLOEXEC SFD_CLOEXEC
#define SFD_NONBLOCK SFD_NONBLOCK
struct signalfd_siginfo
{
uint32_t ssi_signo;
int32_t ssi_errno;
int32_t ssi_code;
uint32_t ssi_pid;
uint32_t ssi_uid;
int32_t ssi_fd;
uint32_t ssi_tid;
uint32_t ssi_band;
uint32_t ssi_overrun;
uint32_t ssi_trapno;
int32_t ssi_status;
int32_t ssi_int;
uint64_t ssi_ptr;
uint64_t ssi_utime;
uint64_t ssi_stime;
uint64_t ssi_addr;
uint8_t pad[48];
};
#ifdef __cplusplus
extern "C" {
#endif
extern int signalfd (int, const sigset_t *, int);
#ifdef __cplusplus
}
#endif
#endif /* _SYS_SIGNALFD_H */

View File

@ -27,7 +27,7 @@ What's new:
- Support overrun counter for posix timers (via timer_getoverrun() or
siginfo_t::si_overrun).
- New API: timer_getoverrun.
- New API: signalfd, timer_getoverrun.
What changed:

View File

@ -1731,3 +1731,68 @@ fhandler_windows::select_except (select_stuff *ss)
s->windows_handle = true;
return s;
}
static int
peek_signalfd (select_record *me, bool)
{
if (((fhandler_signalfd *) me->fh)->poll () == 0)
{
select_printf ("signalfd %d ready", me->fd);
return 1;
}
select_printf ("signalfd %d not ready", me->fd);
return 0;
}
static int
verify_signalfd (select_record *me, fd_set *rfds, fd_set *wfds,
fd_set *efds)
{
return peek_signalfd (me, true);
}
select_record *
fhandler_signalfd::select_read (select_stuff *ss)
{
select_record *s = ss->start.next;
if (!s->startup)
{
s->startup = no_startup;
}
s->verify = verify_signalfd;
s->peek = peek_signalfd;
s->read_selected = true;
s->read_ready = true;
return s;
}
select_record *
fhandler_signalfd::select_write (select_stuff *ss)
{
select_record *s = ss->start.next;
if (!s->startup)
{
s->startup = no_startup;
s->verify = no_verify;
}
s->peek = NULL;
s->write_selected = false;
s->write_ready = false;
return s;
}
select_record *
fhandler_signalfd::select_except (select_stuff *ss)
{
select_record *s = ss->start.next;
if (!s->startup)
{
s->startup = no_startup;
s->verify = no_verify;
}
s->peek = NULL;
s->except_selected = false;
s->except_ready = false;
return s;
}

View File

@ -12,6 +12,7 @@ details. */
#include "winsup.h"
#include <stdlib.h>
#include <sys/cygwin.h>
#include <sys/signalfd.h>
#include "pinfo.h"
#include "sigproc.h"
#include "cygtls.h"
@ -592,7 +593,7 @@ siginterrupt (int sig, int flag)
return res;
}
static inline int
int
sigwait_common (const sigset_t *set, siginfo_t *info, PLARGE_INTEGER waittime)
{
int res = -1;
@ -781,3 +782,62 @@ sigaltstack (const stack_t *ss, stack_t *oss)
__endtry
return 0;
}
extern "C" int
signalfd (int fd_in, const sigset_t *mask, int flags)
{
int ret = -1;
fhandler_signalfd *fh;
debug_printf ("signalfd (%d, %p, %y)", fd_in, mask, flags);
if ((flags & ~(SFD_NONBLOCK | SFD_CLOEXEC)) != 0)
{
set_errno (EINVAL);
goto done;
}
if (fd_in != -1)
{
/* Change signal mask. */
cygheap_fdget fd (fd_in);
if (fd < 0)
goto done;
fh = fd->is_signalfd ();
if (!fh)
{
set_errno (EINVAL);
goto done;
}
__try
{
if (fh->signalfd (mask, flags) == 0)
ret = fd_in;
}
__except (EINVAL) {}
__endtry
}
else
{
/* Create new signalfd descriptor. */
cygheap_fdnew fd;
if (fd < 0)
goto done;
fh = (fhandler_signalfd *) build_fh_dev (*signalfd_dev);
if (fh && fh->signalfd (mask, flags) == 0)
{
fd = fh;
if (fd <= 2)
set_std_handle (fd);
ret = fd;
}
else
delete fh;
}
done:
syscall_printf ("%R = signalfd (%d, %p, %y)", ret, fd_in, mask, flags);
return ret;
}

View File

@ -79,6 +79,7 @@ void __stdcall sigalloc ();
int kill_pgrp (pid_t, siginfo_t&);
void __reg1 exit_thread (DWORD) __attribute__ ((noreturn));
void __reg1 setup_signal_exit (int);
int sigwait_common (const sigset_t *, siginfo_t *, PLARGE_INTEGER);
class no_thread_exit_protect
{

View File

@ -51,7 +51,7 @@ siginfo_t::si_overrun).
</para></listitem>
<listitem><para>
New API: timer_getoverrun.
New API: signalfd, timer_getoverrun.
</para></listitem>
<listitem><para>

View File

@ -1378,6 +1378,7 @@ also IEEE Std 1003.1-2008 (POSIX.1-2008).</para>
scandirat
sched_getcpu
setxattr
signalfd
sincos
sincosf
sincosl