293 lines
6.6 KiB
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;
|
|
}
|