866 lines
18 KiB
C++
866 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 <unistd.h>
|
|
#include <sys/cygwin.h>
|
|
#include <sys/signalfd.h>
|
|
#include <sys/reent.h> /* needed for __stdio_exit_handler declaration */
|
|
#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 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" 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
|
|
handle_sigprocmask (int how, const sigset_t *set, sigset_t *oldset, sigset_t& opmask)
|
|
{
|
|
/* check that how is in right range if set is not NULL */
|
|
if (set && 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
|
|
_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 || !__isthreaded)
|
|
return kill (myself->pid, sig);
|
|
|
|
/* Make sure to return -1 and set errno, as on Linux. */
|
|
int err = pthread_kill (thread, sig);
|
|
if (err)
|
|
set_errno (err);
|
|
return err ? -1 : 0;
|
|
}
|
|
|
|
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 (__stdio_exit_handler)
|
|
(*__stdio_exit_handler) ();
|
|
do_exit (SIGABRT); /* signal handler didn't exit. Goodbye. */
|
|
}
|
|
|
|
static int
|
|
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);
|
|
|
|
do_wait:
|
|
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.current_sig = 0;
|
|
if (_my_tls.retaddr () == (__tlsstack_t) sigdelayed)
|
|
_my_tls.pop ();
|
|
_my_tls.unlock ();
|
|
}
|
|
break;
|
|
case WAIT_TIMEOUT:
|
|
_my_tls.lock ();
|
|
if (_my_tls.sigwait_mask == 0)
|
|
{
|
|
/* sigpacket::process() already started.
|
|
Will surely be signalled soon. */
|
|
waittime = cw_infinite;
|
|
_my_tls.unlock ();
|
|
goto do_wait;
|
|
}
|
|
_my_tls.sigwait_mask = 0;
|
|
_my_tls.unlock ();
|
|
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;
|
|
}
|