mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-01-19 12:59:21 +08:00
41d071d581
Rather than waiting for signalfd_select_wait in a thread, which is racy, create a global event "my_pendingsigs_evt" which is set and reset by wait_sig depending only on the fact if blocked signals are pending or not. This in turn allows to WFMO on this event in select as soon as signalfds are present in the read descriptor set. Select's peek and verify will then check if one of the present signalfds is affected. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
847 lines
18 KiB
C++
847 lines
18 KiB
C++
/* signal.cc
|
|
|
|
Written by Steve Chamberlain of Cygnus Support, sac@cygnus.com
|
|
Significant changes by Sergey Okhapkin <sos@prospect.com.ru>
|
|
|
|
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 <stdlib.h>
|
|
#include <sys/cygwin.h>
|
|
#include <sys/signalfd.h>
|
|
#include "pinfo.h"
|
|
#include "sigproc.h"
|
|
#include "cygtls.h"
|
|
#include "path.h"
|
|
#include "fhandler.h"
|
|
#include "dtable.h"
|
|
#include "cygheap.h"
|
|
#include "cygwait.h"
|
|
#include "posix_timer.h"
|
|
|
|
#define _SA_NORESTART 0x8000
|
|
|
|
static int __reg3 sigaction_worker (int, const struct sigaction *, struct sigaction *, bool);
|
|
|
|
#define sigtrapped(func) ((func) != SIG_IGN && (func) != SIG_DFL)
|
|
|
|
extern "C" _sig_func_ptr
|
|
signal (int sig, _sig_func_ptr func)
|
|
{
|
|
sig_dispatch_pending ();
|
|
_sig_func_ptr prev;
|
|
|
|
/* check that sig is in right range */
|
|
if (sig <= 0 || sig >= NSIG || sig == SIGKILL || sig == SIGSTOP)
|
|
{
|
|
set_errno (EINVAL);
|
|
syscall_printf ("SIG_ERR = signal (%d, %p)", sig, func);
|
|
return (_sig_func_ptr) SIG_ERR;
|
|
}
|
|
|
|
prev = global_sigs[sig].sa_handler;
|
|
struct sigaction& gs = global_sigs[sig];
|
|
if (gs.sa_flags & _SA_NORESTART)
|
|
gs.sa_flags &= ~SA_RESTART;
|
|
else
|
|
gs.sa_flags |= SA_RESTART;
|
|
|
|
gs.sa_mask = SIGTOMASK (sig);
|
|
gs.sa_handler = func;
|
|
gs.sa_flags &= ~SA_SIGINFO;
|
|
|
|
syscall_printf ("%p = signal (%d, %p)", prev, sig, func);
|
|
return prev;
|
|
}
|
|
|
|
extern "C" int
|
|
clock_nanosleep (clockid_t clk_id, int flags, const struct timespec *rqtp,
|
|
struct timespec *rmtp)
|
|
{
|
|
const bool abstime = (flags & TIMER_ABSTIME) ? true : false;
|
|
int res = 0;
|
|
sig_dispatch_pending ();
|
|
pthread_testcancel ();
|
|
|
|
__try
|
|
{
|
|
if (!valid_timespec (*rqtp))
|
|
return EINVAL;
|
|
}
|
|
__except (NO_ERROR)
|
|
{
|
|
return EFAULT;
|
|
}
|
|
__endtry
|
|
|
|
/* Explicitly disallowed by POSIX. Needs to be checked first to avoid
|
|
being caught by the following test. */
|
|
if (clk_id == CLOCK_THREAD_CPUTIME_ID)
|
|
return EINVAL;
|
|
|
|
/* support for CPU-time clocks is optional */
|
|
if (CLOCKID_IS_PROCESS (clk_id) || CLOCKID_IS_THREAD (clk_id))
|
|
return ENOTSUP;
|
|
/* All other valid clocks are valid */
|
|
if (clk_id >= MAX_CLOCKS)
|
|
return EINVAL;
|
|
|
|
LARGE_INTEGER timeout;
|
|
|
|
timeout.QuadPart = (LONGLONG) rqtp->tv_sec * NS100PERSEC
|
|
+ ((LONGLONG) rqtp->tv_nsec + (NSPERSEC/NS100PERSEC) - 1)
|
|
/ (NSPERSEC/NS100PERSEC);
|
|
|
|
if (abstime)
|
|
{
|
|
struct timespec tp;
|
|
|
|
clock_gettime (clk_id, &tp);
|
|
/* Check for immediate timeout */
|
|
if (tp.tv_sec > rqtp->tv_sec
|
|
|| (tp.tv_sec == rqtp->tv_sec && tp.tv_nsec > rqtp->tv_nsec))
|
|
return 0;
|
|
|
|
switch (clk_id)
|
|
{
|
|
case CLOCK_REALTIME_COARSE:
|
|
case CLOCK_REALTIME:
|
|
timeout.QuadPart += FACTOR;
|
|
break;
|
|
default:
|
|
/* other clocks need to be handled with a relative timeout */
|
|
timeout.QuadPart -= tp.tv_sec * NS100PERSEC
|
|
+ tp.tv_nsec / (NSPERSEC/NS100PERSEC);
|
|
timeout.QuadPart *= -1LL;
|
|
break;
|
|
}
|
|
}
|
|
else /* !abstime */
|
|
timeout.QuadPart *= -1LL;
|
|
|
|
syscall_printf ("clock_nanosleep (%ld.%09ld)", rqtp->tv_sec, rqtp->tv_nsec);
|
|
|
|
int rc = cygwait (NULL, &timeout, cw_sig_eintr | cw_cancel | cw_cancel_self);
|
|
if (rc == WAIT_SIGNALED)
|
|
res = EINTR;
|
|
|
|
/* according to POSIX, rmtp is used only if !abstime */
|
|
if (rmtp && !abstime)
|
|
{
|
|
__try
|
|
{
|
|
rmtp->tv_sec = (time_t) (timeout.QuadPart / NS100PERSEC);
|
|
rmtp->tv_nsec = (long) ((timeout.QuadPart % NS100PERSEC)
|
|
* (NSPERSEC/NS100PERSEC));
|
|
}
|
|
__except (NO_ERROR)
|
|
{
|
|
res = EFAULT;
|
|
}
|
|
__endtry
|
|
}
|
|
|
|
syscall_printf ("%d = clock_nanosleep(%lu, %d, %ld.%09ld, %ld.%09.ld)",
|
|
res, clk_id, flags, rqtp->tv_sec, rqtp->tv_nsec,
|
|
rmtp ? rmtp->tv_sec : 0, rmtp ? rmtp->tv_nsec : 0);
|
|
return res;
|
|
}
|
|
|
|
extern "C" int
|
|
nanosleep (const struct timespec *rqtp, struct timespec *rmtp)
|
|
{
|
|
int res = clock_nanosleep (CLOCK_REALTIME, 0, rqtp, rmtp);
|
|
if (res != 0)
|
|
{
|
|
set_errno (res);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
extern "C" unsigned int
|
|
sleep (unsigned int seconds)
|
|
{
|
|
struct timespec req, rem;
|
|
req.tv_sec = seconds;
|
|
req.tv_nsec = 0;
|
|
if (clock_nanosleep (CLOCK_REALTIME, 0, &req, &rem))
|
|
return rem.tv_sec + (rem.tv_nsec > 0);
|
|
return 0;
|
|
}
|
|
|
|
extern "C" unsigned int
|
|
usleep (useconds_t useconds)
|
|
{
|
|
struct timespec req;
|
|
req.tv_sec = useconds / USPERSEC;
|
|
req.tv_nsec = (useconds % USPERSEC) * (NSPERSEC/USPERSEC);
|
|
int res = clock_nanosleep (CLOCK_REALTIME, 0, &req, NULL);
|
|
if (res != 0)
|
|
{
|
|
set_errno (res);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
extern "C" int
|
|
sigprocmask (int how, const sigset_t *set, sigset_t *oldset)
|
|
{
|
|
int res = handle_sigprocmask (how, set, oldset, _my_tls.sigmask);
|
|
if (res)
|
|
{
|
|
set_errno (res);
|
|
res = -1;
|
|
}
|
|
syscall_printf ("%R = sigprocmask (%d, %p, %p)", res, how, set, oldset);
|
|
return res;
|
|
}
|
|
|
|
int __reg3
|
|
handle_sigprocmask (int how, const sigset_t *set, sigset_t *oldset, sigset_t& opmask)
|
|
{
|
|
/* check that how is in right range */
|
|
if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK)
|
|
{
|
|
syscall_printf ("Invalid how value %d", how);
|
|
return EINVAL;
|
|
}
|
|
|
|
__try
|
|
{
|
|
if (oldset)
|
|
*oldset = opmask;
|
|
|
|
if (set)
|
|
{
|
|
sigset_t newmask = opmask;
|
|
switch (how)
|
|
{
|
|
case SIG_BLOCK:
|
|
/* add set to current mask */
|
|
newmask |= *set;
|
|
break;
|
|
case SIG_UNBLOCK:
|
|
/* remove set from current mask */
|
|
newmask &= ~*set;
|
|
break;
|
|
case SIG_SETMASK:
|
|
/* just set it */
|
|
newmask = *set;
|
|
break;
|
|
}
|
|
set_signal_mask (opmask, newmask);
|
|
}
|
|
}
|
|
__except (EFAULT)
|
|
{
|
|
return EFAULT;
|
|
}
|
|
__endtry
|
|
return 0;
|
|
}
|
|
|
|
int __reg2
|
|
_pinfo::kill (siginfo_t& si)
|
|
{
|
|
int res;
|
|
DWORD this_process_state;
|
|
pid_t this_pid;
|
|
|
|
sig_dispatch_pending ();
|
|
|
|
if (exists ())
|
|
{
|
|
bool sendSIGCONT;
|
|
this_process_state = process_state;
|
|
if ((sendSIGCONT = (si.si_signo < 0)))
|
|
si.si_signo = -si.si_signo;
|
|
|
|
if (si.si_signo == 0)
|
|
res = 0;
|
|
else if ((res = (int) sig_send (this, si)))
|
|
{
|
|
sigproc_printf ("%d = sig_send, %E ", res);
|
|
res = -1;
|
|
}
|
|
else if (sendSIGCONT)
|
|
{
|
|
siginfo_t si2 = {0};
|
|
si2.si_signo = SIGCONT;
|
|
si2.si_code = SI_KERNEL;
|
|
sig_send (this, si2);
|
|
}
|
|
this_pid = pid;
|
|
}
|
|
else if (process_state == PID_EXITED)
|
|
{
|
|
this_process_state = process_state;
|
|
this_pid = pid;
|
|
res = 0;
|
|
}
|
|
else
|
|
{
|
|
set_errno (ESRCH);
|
|
this_process_state = 0;
|
|
this_pid = 0;
|
|
res = -1;
|
|
}
|
|
|
|
syscall_printf ("%d = _pinfo::kill (%d), pid %d, process_state %y", res,
|
|
si.si_signo, this_pid, this_process_state);
|
|
return res;
|
|
}
|
|
|
|
extern "C" int
|
|
raise (int sig)
|
|
{
|
|
pthread *thread = _my_tls.tid;
|
|
if (!thread)
|
|
return kill (myself->pid, sig);
|
|
return pthread_kill (thread, sig);
|
|
}
|
|
|
|
static int
|
|
kill0 (pid_t pid, siginfo_t& si)
|
|
{
|
|
syscall_printf ("kill (%d, %d)", pid, si.si_signo);
|
|
/* check that sig is in right range */
|
|
if (si.si_signo < 0 || si.si_signo >= NSIG)
|
|
{
|
|
set_errno (EINVAL);
|
|
syscall_printf ("signal %d out of range", si.si_signo);
|
|
return -1;
|
|
}
|
|
if (pid > 0)
|
|
{
|
|
pinfo p (pid);
|
|
if (!p)
|
|
{
|
|
set_errno (ESRCH);
|
|
return -1;
|
|
}
|
|
return p->kill (si);
|
|
}
|
|
return kill_pgrp (-pid, si);
|
|
}
|
|
|
|
int
|
|
kill (pid_t pid, int sig)
|
|
{
|
|
siginfo_t si = {0};
|
|
si.si_signo = sig;
|
|
si.si_code = SI_USER;
|
|
return kill0 (pid, si);
|
|
}
|
|
|
|
int
|
|
kill_pgrp (pid_t pid, siginfo_t& si)
|
|
{
|
|
int res = 0;
|
|
int found = 0;
|
|
int killself = 0;
|
|
|
|
sigproc_printf ("pid %d, signal %d", pid, si.si_signo);
|
|
|
|
winpids pids ((DWORD) PID_MAP_RW);
|
|
for (unsigned i = 0; i < pids.npids; i++)
|
|
{
|
|
_pinfo *p = pids[i];
|
|
|
|
if (!p || !p->exists ())
|
|
continue;
|
|
|
|
/* Is it a process we want to kill? */
|
|
if ((pid == 0 && (p->pgid != myself->pgid || p->ctty != myself->ctty)) ||
|
|
(pid > 1 && p->pgid != pid) ||
|
|
(si.si_signo < 0 && NOTSTATE (p, PID_STOPPED)))
|
|
continue;
|
|
sigproc_printf ("killing pid %d, pgrp %d, p->%s, %s", p->pid, p->pgid,
|
|
p->__ctty (), myctty ());
|
|
if (p == myself)
|
|
killself++;
|
|
else if (p->kill (si))
|
|
res = -1;
|
|
found++;
|
|
}
|
|
|
|
if (killself && !exit_state && myself->kill (si))
|
|
res = -1;
|
|
|
|
if (!found)
|
|
{
|
|
set_errno (ESRCH);
|
|
res = -1;
|
|
}
|
|
syscall_printf ("%R = kill(%d, %d)", res, pid, si.si_signo);
|
|
return res;
|
|
}
|
|
|
|
extern "C" int
|
|
killpg (pid_t pgrp, int sig)
|
|
{
|
|
return kill (-pgrp, sig);
|
|
}
|
|
|
|
extern "C" void
|
|
abort (void)
|
|
{
|
|
_my_tls.incyg++;
|
|
sig_dispatch_pending ();
|
|
/* Ensure that SIGABRT can be caught regardless of blockage. */
|
|
sigset_t sig_mask;
|
|
sigfillset (&sig_mask);
|
|
sigdelset (&sig_mask, SIGABRT);
|
|
set_signal_mask (_my_tls.sigmask, sig_mask);
|
|
|
|
raise (SIGABRT);
|
|
_my_tls.call_signal_handler (); /* Call any signal handler */
|
|
|
|
/* Flush all streams as per SUSv2. */
|
|
if (_GLOBAL_REENT->__cleanup)
|
|
_GLOBAL_REENT->__cleanup (_GLOBAL_REENT);
|
|
do_exit (SIGABRT); /* signal handler didn't exit. Goodbye. */
|
|
}
|
|
|
|
static int __reg3
|
|
sigaction_worker (int sig, const struct sigaction *newact,
|
|
struct sigaction *oldact, bool isinternal)
|
|
{
|
|
int res = -1;
|
|
__try
|
|
{
|
|
sig_dispatch_pending ();
|
|
/* check that sig is in right range */
|
|
if (sig <= 0 || sig >= NSIG)
|
|
set_errno (EINVAL);
|
|
else
|
|
{
|
|
struct sigaction oa = global_sigs[sig];
|
|
|
|
if (!newact)
|
|
sigproc_printf ("signal %d, newact %p, oa %p",
|
|
sig, newact, oa, oa.sa_handler);
|
|
else
|
|
{
|
|
sigproc_printf ("signal %d, newact %p (handler %p), oa %p",
|
|
sig, newact, newact->sa_handler, oa,
|
|
oa.sa_handler);
|
|
if (sig == SIGKILL || sig == SIGSTOP)
|
|
{
|
|
set_errno (EINVAL);
|
|
__leave;
|
|
}
|
|
struct sigaction na = *newact;
|
|
struct sigaction& gs = global_sigs[sig];
|
|
if (!isinternal)
|
|
na.sa_flags &= ~_SA_INTERNAL_MASK;
|
|
gs = na;
|
|
if (!(gs.sa_flags & SA_NODEFER))
|
|
gs.sa_mask |= SIGTOMASK(sig);
|
|
if (gs.sa_handler == SIG_IGN)
|
|
sig_clear (sig);
|
|
if (gs.sa_handler == SIG_DFL && sig == SIGCHLD)
|
|
sig_clear (sig);
|
|
if (sig == SIGCHLD)
|
|
{
|
|
myself->process_state &= ~PID_NOCLDSTOP;
|
|
if (gs.sa_flags & SA_NOCLDSTOP)
|
|
myself->process_state |= PID_NOCLDSTOP;
|
|
}
|
|
}
|
|
|
|
if (oldact)
|
|
{
|
|
*oldact = oa;
|
|
oa.sa_flags &= ~_SA_INTERNAL_MASK;
|
|
}
|
|
res = 0;
|
|
}
|
|
}
|
|
__except (EFAULT) {}
|
|
__endtry
|
|
return res;
|
|
}
|
|
|
|
extern "C" int
|
|
sigaction (int sig, const struct sigaction *newact, struct sigaction *oldact)
|
|
{
|
|
int res = sigaction_worker (sig, newact, oldact, false);
|
|
syscall_printf ("%R = sigaction(%d, %p, %p)", res, sig, newact, oldact);
|
|
return res;
|
|
}
|
|
|
|
extern "C" int
|
|
sigaddset (sigset_t *set, const int sig)
|
|
{
|
|
/* check that sig is in right range */
|
|
if (sig <= 0 || sig >= NSIG)
|
|
{
|
|
set_errno (EINVAL);
|
|
syscall_printf ("SIG_ERR = sigaddset signal %d out of range", sig);
|
|
return -1;
|
|
}
|
|
|
|
*set |= SIGTOMASK (sig);
|
|
return 0;
|
|
}
|
|
|
|
extern "C" int
|
|
sigdelset (sigset_t *set, const int sig)
|
|
{
|
|
/* check that sig is in right range */
|
|
if (sig <= 0 || sig >= NSIG)
|
|
{
|
|
set_errno (EINVAL);
|
|
syscall_printf ("SIG_ERR = sigdelset signal %d out of range", sig);
|
|
return -1;
|
|
}
|
|
|
|
*set &= ~SIGTOMASK (sig);
|
|
return 0;
|
|
}
|
|
|
|
extern "C" int
|
|
sigismember (const sigset_t *set, int sig)
|
|
{
|
|
/* check that sig is in right range */
|
|
if (sig <= 0 || sig >= NSIG)
|
|
{
|
|
set_errno (EINVAL);
|
|
syscall_printf ("SIG_ERR = sigdelset signal %d out of range", sig);
|
|
return -1;
|
|
}
|
|
|
|
if (*set & SIGTOMASK (sig))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
extern "C" int
|
|
sigemptyset (sigset_t *set)
|
|
{
|
|
*set = (sigset_t) 0;
|
|
return 0;
|
|
}
|
|
|
|
extern "C" int
|
|
sigfillset (sigset_t *set)
|
|
{
|
|
*set = ~((sigset_t) 0);
|
|
return 0;
|
|
}
|
|
|
|
extern "C" int
|
|
sigsuspend (const sigset_t *set)
|
|
{
|
|
int res = handle_sigsuspend (*set);
|
|
syscall_printf ("%R = sigsuspend(%p)", res, set);
|
|
return res;
|
|
}
|
|
|
|
extern "C" int
|
|
sigpause (int signal_mask)
|
|
{
|
|
int res = handle_sigsuspend ((sigset_t) signal_mask);
|
|
syscall_printf ("%R = sigpause(%y)", res, signal_mask);
|
|
return res;
|
|
}
|
|
|
|
extern "C" int
|
|
__xpg_sigpause (int sig)
|
|
{
|
|
int res;
|
|
sigset_t signal_mask;
|
|
sigprocmask (0, NULL, &signal_mask);
|
|
sigdelset (&signal_mask, sig);
|
|
res = handle_sigsuspend (signal_mask);
|
|
syscall_printf ("%R = __xpg_sigpause(%y)", res, sig);
|
|
return res;
|
|
}
|
|
|
|
extern "C" int
|
|
pause (void)
|
|
{
|
|
int res = handle_sigsuspend (_my_tls.sigmask);
|
|
syscall_printf ("%R = pause()", res);
|
|
return res;
|
|
}
|
|
|
|
extern "C" int
|
|
siginterrupt (int sig, int flag)
|
|
{
|
|
struct sigaction act;
|
|
int res = sigaction_worker (sig, NULL, &act, false);
|
|
if (res == 0)
|
|
{
|
|
if (flag)
|
|
{
|
|
act.sa_flags &= ~SA_RESTART;
|
|
act.sa_flags |= _SA_NORESTART;
|
|
}
|
|
else
|
|
{
|
|
act.sa_flags &= ~_SA_NORESTART;
|
|
act.sa_flags |= SA_RESTART;
|
|
}
|
|
res = sigaction_worker (sig, &act, NULL, true);
|
|
}
|
|
syscall_printf ("%R = siginterrupt(%d, %y)", sig, flag);
|
|
return res;
|
|
}
|
|
|
|
int
|
|
sigwait_common (const sigset_t *set, siginfo_t *info, PLARGE_INTEGER waittime)
|
|
{
|
|
int res = -1;
|
|
|
|
pthread_testcancel ();
|
|
|
|
__try
|
|
{
|
|
set_signal_mask (_my_tls.sigwait_mask, *set);
|
|
sig_dispatch_pending (true);
|
|
|
|
switch (cygwait (NULL, waittime,
|
|
cw_sig_eintr | cw_cancel | cw_cancel_self))
|
|
{
|
|
case WAIT_SIGNALED:
|
|
if (!sigismember (set, _my_tls.infodata.si_signo))
|
|
set_errno (EINTR);
|
|
else
|
|
{
|
|
_my_tls.lock ();
|
|
if (_my_tls.infodata.si_code == SI_TIMER)
|
|
{
|
|
timer_tracker *tt = (timer_tracker *)
|
|
_my_tls.infodata.si_tid;
|
|
_my_tls.infodata.si_overrun = tt->disarm_overrun_event ();
|
|
}
|
|
if (info)
|
|
*info = _my_tls.infodata;
|
|
res = _my_tls.infodata.si_signo;
|
|
_my_tls.sig = 0;
|
|
if (_my_tls.retaddr () == (__tlsstack_t) sigdelayed)
|
|
_my_tls.pop ();
|
|
_my_tls.unlock ();
|
|
}
|
|
break;
|
|
case WAIT_TIMEOUT:
|
|
set_errno (EAGAIN);
|
|
break;
|
|
default:
|
|
__seterrno ();
|
|
break;
|
|
}
|
|
}
|
|
__except (EFAULT)
|
|
{
|
|
res = -1;
|
|
}
|
|
__endtry
|
|
sigproc_printf ("returning signal %d", res);
|
|
return res;
|
|
}
|
|
|
|
extern "C" int
|
|
sigtimedwait (const sigset_t *set, siginfo_t *info, const timespec *timeout)
|
|
{
|
|
LARGE_INTEGER waittime;
|
|
|
|
if (timeout)
|
|
{
|
|
if (!valid_timespec (*timeout))
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
/* convert timespec to 100ns units */
|
|
waittime.QuadPart = (LONGLONG) timeout->tv_sec * NS100PERSEC
|
|
+ ((LONGLONG) timeout->tv_nsec + (NSPERSEC/NS100PERSEC) - 1)
|
|
/ (NSPERSEC/NS100PERSEC);
|
|
/* negate waittime to code as duration for NtSetTimer() below cygwait() */
|
|
waittime.QuadPart = -waittime.QuadPart;
|
|
}
|
|
|
|
return sigwait_common (set, info, timeout ? &waittime : cw_infinite);
|
|
}
|
|
|
|
extern "C" int
|
|
sigwait (const sigset_t *set, int *sig_ptr)
|
|
{
|
|
int sig;
|
|
|
|
do
|
|
{
|
|
sig = sigwait_common (set, NULL, cw_infinite);
|
|
}
|
|
while (sig == -1 && get_errno () == EINTR);
|
|
if (sig > 0)
|
|
*sig_ptr = sig;
|
|
return sig > 0 ? 0 : get_errno ();
|
|
}
|
|
|
|
extern "C" int
|
|
sigwaitinfo (const sigset_t *set, siginfo_t *info)
|
|
{
|
|
return sigwait_common (set, info, cw_infinite);
|
|
}
|
|
|
|
/* FIXME: SUSv3 says that this function should block until the signal has
|
|
actually been delivered. Currently, this will only happen when sending
|
|
signals to the current process. It will not happen when sending signals
|
|
to other processes. */
|
|
extern "C" int
|
|
sigqueue (pid_t pid, int sig, const union sigval value)
|
|
{
|
|
siginfo_t si = {0};
|
|
pinfo dest (pid);
|
|
if (!dest)
|
|
{
|
|
set_errno (ESRCH);
|
|
return -1;
|
|
}
|
|
if (sig == 0)
|
|
return 0;
|
|
if (sig < 0 || sig >= NSIG)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
si.si_signo = sig;
|
|
si.si_code = SI_QUEUE;
|
|
si.si_value = value;
|
|
return (int) sig_send (dest, si);
|
|
}
|
|
|
|
extern "C" int
|
|
sigaltstack (const stack_t *ss, stack_t *oss)
|
|
{
|
|
_cygtls& me = _my_tls;
|
|
|
|
__try
|
|
{
|
|
if (ss)
|
|
{
|
|
if (me.altstack.ss_flags == SS_ONSTACK)
|
|
{
|
|
/* An attempt was made to modify an active stack. */
|
|
set_errno (EPERM);
|
|
return -1;
|
|
}
|
|
if (ss->ss_flags == SS_DISABLE)
|
|
{
|
|
me.altstack.ss_sp = NULL;
|
|
me.altstack.ss_flags = 0;
|
|
me.altstack.ss_size = 0;
|
|
}
|
|
else
|
|
{
|
|
if (ss->ss_flags)
|
|
{
|
|
/* The ss argument is not a null pointer, and the ss_flags
|
|
member pointed to by ss contains flags other than
|
|
SS_DISABLE. */
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
if (ss->ss_size < MINSIGSTKSZ)
|
|
{
|
|
/* The size of the alternate stack area is less than
|
|
MINSIGSTKSZ. */
|
|
set_errno (ENOMEM);
|
|
return -1;
|
|
}
|
|
memcpy (&me.altstack, ss, sizeof *ss);
|
|
}
|
|
}
|
|
if (oss)
|
|
{
|
|
char stack_marker;
|
|
memcpy (oss, &me.altstack, sizeof *oss);
|
|
/* Check if the current stack is the alternate signal stack. If so,
|
|
set ss_flags accordingly. We do this here rather than setting
|
|
ss_flags in _cygtls::call_signal_handler since the signal handler
|
|
calls longjmp, so we never return to reset the flag. */
|
|
if (!me.altstack.ss_flags && me.altstack.ss_sp)
|
|
{
|
|
if (&stack_marker >= (char *) me.altstack.ss_sp
|
|
&& &stack_marker < (char *) me.altstack.ss_sp
|
|
+ me.altstack.ss_size)
|
|
oss->ss_flags = SS_ONSTACK;
|
|
}
|
|
}
|
|
}
|
|
__except (EFAULT)
|
|
{
|
|
return EFAULT;
|
|
}
|
|
__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;
|
|
}
|