jehanne/sys/src/lib/posix/sigsets.c

293 lines
6.6 KiB
C

/*
* This file is part of Jehanne.
*
* Copyright (C) 2017 Giacomo Tesio <giacomo@tesio.it>
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, version 3 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
#include <u.h>
#include <lib9.h>
#include <posix.h>
#include "internal.h"
typedef union {
PosixSignalMask signals;
char raw[sizeof(PosixSignalMask)];
} SigSetBuf;
extern SignalConf *__libposix_signals;
extern int *__restart_syscall;
extern int *__libposix_devsignal;
PosixSignalMask *__libposix_signal_mask;
static PendingSignalList *__libposix_pending_signals;
static PendingSignalList **__libposix_pending_signals_end;
static int __libposix_blocked_signals;
void
__libposix_reset_pending_signals(void)
{
PendingSignalList *s;
while((s = __libposix_pending_signals) != nil){
__libposix_pending_signals = s->next;
free(s);
}
__libposix_pending_signals_end = &__libposix_pending_signals;
*__restart_syscall = 0;
*__libposix_signal_mask = 0;
}
int
__libposix_signal_blocked(PosixSignalInfo *siginfo)
{
PendingSignalList *l;
if((*__libposix_signal_mask & SIGNAL_MASK(siginfo->si_signo)) == 0)
return 0;
/* Blocked signals are recorded at __libposix_pending_signals
* and will be delivered by sigprocmask when they will
* be unblocked
*/
l = malloc(sizeof(PendingSignalList));
if(l == nil)
return 1; /* signal discarded */
memmove(&l->signal, siginfo, sizeof(PosixSignalInfo));
l->next = nil;
*__libposix_pending_signals_end = l;
__libposix_pending_signals_end = &l->next;
*__restart_syscall = 1;
++__libposix_blocked_signals;
return 1;
}
int
POSIX_sigaddset(int *errnop, PosixSignalMask *set, int signo)
{
if(signo < 1 || signo > PosixNumberOfSignals){
*errnop = __libposix_get_errno(PosixEINVAL);
return -1;
}
SIGNAL_RAW_ADD(*set, signo);
return 0;
}
int
POSIX_sigdelset(int *errnop, PosixSignalMask *set, int signo)
{
if(signo < 1 || signo > PosixNumberOfSignals){
*errnop = __libposix_get_errno(PosixEINVAL);
return -1;
}
SIGNAL_RAW_DEL(*set, signo);
return 0;
}
int
POSIX_sigismember(int *errnop, const PosixSignalMask *set, int signo)
{
if(signo < 1 || signo > PosixNumberOfSignals){
*errnop = __libposix_get_errno(PosixEINVAL);
return -1;
}
if(*set & SIGNAL_MASK(signo))
return 1;
return 0;
}
int
POSIX_sigfillset(int *errnop, PosixSignalMask *set)
{
*set = ~0;
return 0;
}
int
POSIX_sigemptyset(int *errnop, PosixSignalMask *set)
{
*set = 0;
return 0;
}
int
POSIX_sigpending(int *errnop, PosixSignalMask *set)
{
PendingSignalList *p;
PosixSignalMask tmp;
if(set == nil){
*errnop = __libposix_get_errno(PosixEFAULT);
return -1;
}
tmp = __libposix_sighelper_cmd(PHGetPendingSignals, 0);
/* include pending signals from outside the session */
p = __libposix_pending_signals;
while(p != nil){
tmp |= SIGNAL_MASK(p->signal.si_signo);
p = p->next;
}
*set = tmp;
return 0;
}
long
__libposix_sighelper_wait(PosixSignalMask set, PosixSignalInfo *siginfo)
{
return pread(*__libposix_devsignal, siginfo, sizeof(PosixSignalInfo), set);
}
int
POSIX_sigtimedwait(int *errnop, const PosixSignalMask *set,
PosixSignalInfo *info, const struct timespec *timeout)
{
long wkp = 0, r;
PendingSignalList *p, **end;
PosixSignalInfo tmp;
PosixError e = 0;
int ms = -1, bs;
if(set == nil){
e = PosixEFAULT;
goto FailWithError;
}
if(timeout != nil){
if(timeout->tv_sec < 0 || timeout->tv_nsec < 0){
e = PosixEINVAL;
goto FailWithError;
}
ms = timeout->tv_sec * 1000;
ms += timeout->tv_nsec / 1000000;
}
if(info == nil){
memset(&tmp, 0, sizeof(PosixSignalInfo));
info = &tmp;
}
LookupPendingSignals:
bs = __libposix_blocked_signals;
end = &__libposix_pending_signals;
for(p = *end; p != nil; p = *end){
if((*set & SIGNAL_MASK(p->signal.si_signo)) != 0){
memcpy(info, &p->signal, sizeof(PosixSignalInfo));
*end = p->next;
free(p);
if(__libposix_pending_signals == nil)
__libposix_pending_signals_end = &__libposix_pending_signals;
goto Done;
}
end = &p->next;
}
if(bs != __libposix_blocked_signals)
goto LookupPendingSignals;
if(ms == 0){
/* ms == 0 means that timeout has both fields to zero */
if(__libposix_sighelper_cmd(PHGetPendingSignals, 0) == 0){
e = PosixEAGAIN;
goto FailWithError;
}
}
if(ms > 0)
wkp = awake(ms);
r = __libposix_sighelper_wait(*set, info);
if(r < 0){
if(ms > 0 && awakened(wkp)){
/* timed out */
e = PosixEAGAIN;
goto FailWithError;
}
if(bs != __libposix_blocked_signals){
LookupPendingSignals2:
bs = __libposix_blocked_signals;
end = &__libposix_pending_signals;
for(p = *end; p != nil; p = *end){
if((*set & SIGNAL_MASK(p->signal.si_signo)) != 0){
memcpy(info, &p->signal, sizeof(PosixSignalInfo));
*end = p->next;
free(p);
if(__libposix_pending_signals == nil)
__libposix_pending_signals_end = &__libposix_pending_signals;
goto Done;
}
end = &p->next;
}
if(bs != __libposix_blocked_signals)
goto LookupPendingSignals2;
}
e = PosixEINTR;
goto FailWithError;
}
if(ms > 0)
forgivewkp(wkp);
Done:
return info->si_signo;
FailWithError:
*errnop = __libposix_get_errno(e);
return -1;
}
int
POSIX_sigprocmask(int *errnop, PosixSigProcMaskAction how, const PosixSignalMask *set, PosixSignalMask *oset)
{
PendingSignalList *p, **e;
PosixSignalMask new;
PosixSignalMask old = *__libposix_signal_mask;
int sigindex;
if(set){
new = *set;
new &= ~((255UL<<56) | (PosixSIGKILL-1) | (PosixSIGSTOP-1));
switch(how){
case PosixSPMSetMask:
*__libposix_signal_mask = new;
break;
case PosixSPMBlock:
*__libposix_signal_mask |= new;
break;
case PosixSPMUnblock:
*__libposix_signal_mask &= ~(new);
break;
default:
*errnop = __libposix_get_errno(PosixEINVAL);
return -1;
}
__libposix_sighelper_set(PHBlockSignals, *__libposix_signal_mask);
}
if(oset)
*oset = old;
new = *__libposix_signal_mask;
if(old & (~new)){
e = &__libposix_pending_signals;
for(p = *e; p != nil; p = *e){
sigindex = p->signal.si_signo - 1;
if((new & (1ULL << sigindex)) == 0){
__libposix_run_signal_handler(__libposix_signals + sigindex, &p->signal);
*e = p->next;
free(p);
} else {
e = &p->next;
}
}
}
return 0;
}