/* * This file is part of Jehanne. * * Copyright (C) 2017 Giacomo Tesio * * 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 . */ #include #include #include #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; }