1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-15 10:17:20 +00:00
freebsd/sys/kern/subr_sigq.c

302 lines
6.2 KiB
C

/*-
* Copyright (c) 2002 New Gold Technology. All rights reserved.
* Copyright (c) 2002 Juli Mallett. All rights reserved.
*
* This software was written by Juli Mallett <jmallett@FreeBSD.org> for the
* FreeBSD project. Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/signalvar.h>
#include <sys/proc.h>
#include <sys/errno.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/signal.h>
#include <sys/ksiginfo.h>
MALLOC_DEFINE(M_KSIGINFO, "ksiginfos", "Kernel signal info structures");
int
ksiginfo_alloc(struct ksiginfo **ksip, int signo)
{
int error;
struct ksiginfo *ksi;
error = 0;
ksi = malloc(sizeof *ksi, M_KSIGINFO, M_ZERO | M_WAITOK);
KASSERT(ksi != NULL, ("ksiginfo_alloc(%d): allocation failed.", signo));
if (ksi == NULL) {
error = ENOMEM;
goto out;
}
ksi->ksi_signo = signo;
if (curproc != NULL) {
ksi->ksi_pid = curproc->p_pid;
ksi->ksi_ruid = curproc->p_ucred->cr_uid;
}
out:
*ksip = ksi;
return (error);
}
int
ksiginfo_dequeue(struct ksiginfo **ksip, struct proc *p, int signo)
{
int error;
struct ksiginfo *ksi;
error = 0;
ksi = NULL;
PROC_LOCK_ASSERT(p, MA_OWNED);
if (TAILQ_EMPTY(&p->p_sigq)) {
error = EDOOFUS;
goto out;
}
if (!signo) {
ksi = TAILQ_FIRST(&p->p_sigq);
goto out;
}
TAILQ_FOREACH(ksi, &p->p_sigq, ksi_queue) {
if (ksi->ksi_signo == signo)
goto out;
}
error = ESRCH;
ksi = NULL;
out:
if (ksi != NULL)
TAILQ_REMOVE(&p->p_sigq, ksi, ksi_queue);
*ksip = ksi;
return (error);
}
int
ksiginfo_destroy(struct ksiginfo **ksip)
{
int error;
struct ksiginfo *ksi;
error = 0;
ksi = *ksip;
if (ksi == NULL) {
error = EDOOFUS;
goto out;
}
free(ksi, M_KSIGINFO);
ksi = NULL;
out:
*ksip = ksi;
return (error);
}
int
ksiginfo_to_sigset_t(struct proc *p, sigset_t *setp)
{
int error;
sigset_t set;
struct ksiginfo *ksi;
error = 0;
SIGEMPTYSET(set);
PROC_LOCK_ASSERT(p, MA_OWNED);
/*
* We could set EDOOFUS here, however if there are no queued
* signals, then an empty signal set _is_ valid.
*/
if (TAILQ_EMPTY(&p->p_sigq))
goto out;
TAILQ_FOREACH(ksi, &p->p_sigq, ksi_queue)
SIGADDSET(set, ksi->ksi_signo);
out:
*setp = set;
return (error);
}
int
signal_add(struct proc *p, struct ksiginfo *ksi, int signo)
{
int error;
error = 0;
PROC_LOCK_ASSERT(p, MA_OWNED);
if (ksi == NULL) {
PROC_UNLOCK(p);
error = ksiginfo_alloc(&ksi, signo);
PROC_LOCK(p);
if (error)
goto out;
}
TAILQ_INSERT_HEAD(&p->p_sigq, ksi, ksi_queue);
out:
return (error);
}
int
signal_delete(struct proc *p, struct ksiginfo *ksi, int signo)
{
int error;
error = 0;
PROC_LOCK_ASSERT(p, MA_OWNED);
if (ksi == NULL) {
while (signal_queued(p, signo)) {
error = ksiginfo_dequeue(&ksi, p, signo);
if (error)
goto out;
error = ksiginfo_destroy(&ksi);
if (error)
goto out;
}
}
out:
if (ksi != NULL) {
TAILQ_REMOVE(&p->p_sigq, ksi, ksi_queue);
ksiginfo_destroy(&ksi);
}
return (error);
}
int
signal_delete_mask(struct proc *p, int mask)
{
int error;
struct ksiginfo *ksi, *prev;
error = 0;
ksi = prev = NULL;
PROC_LOCK_ASSERT(p, MA_OWNED);
if (TAILQ_EMPTY(&p->p_sigq))
goto out;
TAILQ_FOREACH(ksi, &p->p_sigq, ksi_queue) {
if (prev != NULL) {
TAILQ_REMOVE(&p->p_sigq, prev, ksi_queue);
error = ksiginfo_destroy(&prev);
if (error)
goto out;
}
if (sigmask(ksi->ksi_signo) & mask)
prev = ksi;
}
if (prev != NULL) {
TAILQ_REMOVE(&p->p_sigq, prev, ksi_queue);
error = ksiginfo_destroy(&prev);
/*
* XXX - Could just fall off the bottom...
*/
if (error)
goto out;
}
out:
return (error);
}
int
signal_pending(struct proc *p)
{
int error, pending;
sigset_t set;
error = 0;
pending = 0;
PROC_LOCK_ASSERT(p, MA_OWNED);
if (TAILQ_EMPTY(&p->p_sigq))
goto out;
if (p->p_flag & P_TRACED) {
pending = 1;
goto out;
}
error = ksiginfo_to_sigset_t(p, &set);
if (error)
goto out;
pending = !sigsetmasked(&set, &p->p_sigmask);
if (pending)
goto out;
out:
return (pending);
}
int
signal_queued(struct proc *p, int signo)
{
int error, pending;
struct ksiginfo *ksi;
error = 0;
pending = 0;
ksi = NULL;
PROC_LOCK_ASSERT(p, MA_OWNED);
if (TAILQ_EMPTY(&p->p_sigq))
goto out;
/*
* Since we know the queue is not empty, we can just do
* pending = TAILQ_FIRST(&p->p_sigq)->ksi_signo, if the
* signo is 0, however since we have to use at least one
* more TailQ manipulation macro either way, might as well
* just do it like this, as I think it optimises better,
* even if the failure case is more expensive.
*/
TAILQ_FOREACH(ksi, &p->p_sigq, ksi_queue) {
pending = !signo || ksi->ksi_signo == signo;
if (pending)
goto out;
}
out:
return (pending);
}
int
signal_queued_mask(struct proc *p, sigset_t mask)
{
int pending;
struct ksiginfo *ksi;
pending = 0;
ksi = NULL;
PROC_LOCK_ASSERT(p, MA_OWNED);
if (TAILQ_EMPTY(&p->p_sigq))
goto out;
TAILQ_FOREACH(ksi, &p->p_sigq, ksi_queue) {
if (SIGISMEMBER(mask, ksi->ksi_signo)) {
pending = ksi->ksi_signo;
goto out;
}
}
out:
return (pending);
}