Add sigsetop extensions commonly found in musl libc and glibc

These functions (sigandset, sigisemptyset, sigorset) are commonly available
in at least musl libc and glibc; sigorset, at least, has proven quite useful
in qemu-bsd-user work for tracking the current process signal mask in a more
self-documenting/aesthetically pleasing manner.

Reviewed by:	bapt, jilles, pfg
MFC after:	1 week
Differential Revision:	https://reviews.freebsd.org/D22187
This commit is contained in:
kevans 2019-12-12 01:41:55 +00:00
parent 0b8890615f
commit 0cae8c4a40
7 changed files with 288 additions and 4 deletions

View File

@ -122,7 +122,10 @@ void psignal(int, const char *);
#endif
#if __BSD_VISIBLE
int sigandset(sigset_t *dest, const sigset_t *left, const sigset_t *right);
int sigblock(int);
int sigisemptyset(const sigset_t *set);
int sigorset(sigset_t *dest, const sigset_t *left, const sigset_t *right);
int sigreturn(const struct __ucontext *);
int sigsetmask(int);
int sigstack(const struct sigstack *, struct sigstack *);

View File

@ -497,10 +497,13 @@ MLINKS+=setjmp.3 _longjmp.3 \
MLINKS+=setmode.3 getmode.3
MLINKS+=setproctitle.3 setproctitle_fast.3
MLINKS+=sigsetops.3 sigaddset.3 \
sigsetops.3 sigandset.3 \
sigsetops.3 sigdelset.3 \
sigsetops.3 sigemptyset.3 \
sigsetops.3 sigfillset.3 \
sigsetops.3 sigismember.3
sigsetops.3 sigisemptyset.3 \
sigsetops.3 sigismember.3 \
sigsetops.3 sigorset.3
MLINKS+=statvfs.3 fstatvfs.3
MLINKS+=stringlist.3 sl_add.3 \
stringlist.3 sl_find.3 \

View File

@ -422,6 +422,12 @@ FBSD_1.5 {
timespec_get;
};
FBSD_1.6 {
sigandset;
sigisemptyset;
sigorset;
};
FBSDprivate_1.0 {
/* needed by thread libraries */
__thr_jtable;

View File

@ -28,15 +28,18 @@
.\" @(#)sigsetops.3 8.1 (Berkeley) 6/4/93
.\" $FreeBSD$
.\"
.Dd December 16, 2004
.Dd October 29, 2019
.Dt SIGSETOPS 3
.Os
.Sh NAME
.Nm sigemptyset ,
.Nm sigfillset ,
.Nm sigaddset ,
.Nm sigandset ,
.Nm sigdelset ,
.Nm sigismember
.Nm sigisemptyset ,
.Nm sigismember ,
.Nm sigorset
.Nd manipulate signal sets
.Sh LIBRARY
.Lb libc
@ -49,9 +52,15 @@
.Ft int
.Fn sigaddset "sigset_t *set" "int signo"
.Ft int
.Fn sigandset "sigset_t *set" "const sigset_t *left" "const sigset_t *right"
.Ft int
.Fn sigdelset "sigset_t *set" "int signo"
.Ft int
.Fn sigisemptyset "const sigset_t *set"
.Ft int
.Fn sigismember "const sigset_t *set" "int signo"
.Ft int
.Fn sigorset "sigset_t *set" "const sigset_t *left" "const sigset_t *right"
.Sh DESCRIPTION
These functions manipulate signal sets stored in a
.Fa sigset_t .
@ -78,22 +87,54 @@ function adds the specified signal
to the signal set.
.Pp
The
.Fn sigandset
function sets the specified
.Fa set
to the logical AND of all signals from the
.Fa left
and
.Fa right
signal sets.
.Pp
The
.Fn sigdelset
function deletes the specified signal
.Fa signo
from the signal set.
.Pp
The
.Fn sigisemptyset
function returns whether the specified
.Fa set
is empty or not.
.Pp
The
.Fn sigismember
function returns whether a specified signal
.Fa signo
is contained in the signal set.
.Pp
The
.Fn sigorset
function sets the specified
.Fa set
to the logical OR of all signals from the
.Fa left
and
.Fa right
signal sets.
.Sh RETURN VALUES
The
.Fn sigisemptyset
function returns 1
if the set is empty, 0 otherwise.
.Pp
The
.Fn sigismember
function returns 1
if the signal is a member of the set,
0 otherwise.
.Pp
The other functions return 0 upon success.
A \-1 return value
indicates an error occurred and the global variable
@ -113,5 +154,13 @@ has an invalid value.
.Xr sigprocmask 2 ,
.Xr sigsuspend 2
.Sh STANDARDS
These functions are defined by
The
.Fn sigandset ,
.Fn sigisemptyset ,
and
.Fn sigorset
functions are FreeBSD extensions, compatible with functions of the same name
provided by both musl libc and GNU libc.
.Pp
The rest of these functions are defined by
.St -p1003.1-88 .

View File

@ -80,6 +80,37 @@ sigfillset(sigset_t *set)
return (0);
}
int
sigorset(sigset_t *dest, const sigset_t *left, const sigset_t *right)
{
int i;
for (i = 0; i < _SIG_WORDS; i++)
dest->__bits[i] = left->__bits[i] | right->__bits[i];
return (0);
}
int
sigandset(sigset_t *dest, const sigset_t *left, const sigset_t *right)
{
int i;
for (i = 0; i < _SIG_WORDS; i++)
dest->__bits[i] = left->__bits[i] & right->__bits[i];
return (0);
}
int
sigisemptyset(const sigset_t *set)
{
int i;
for (i = 0; i < _SIG_WORDS; i++)
if (set->__bits[i] != 0)
return (0);
return (1);
}
int
sigismember(const sigset_t *set, int signo)
{

View File

@ -17,6 +17,7 @@ ATF_TESTS_C+= makecontext_test
ATF_TESTS_C+= popen_test
ATF_TESTS_C+= posix_spawn_test
ATF_TESTS_C+= realpath2_test
ATF_TESTS_C+= sigsetops_test
ATF_TESTS_C+= wordexp_test
# TODO: t_closefrom, t_cpuset, t_fmtcheck, t_randomid,

View File

@ -0,0 +1,191 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <atf-c.h>
/* Return the status of the specified sig's bit. */
static bool
sigbitstatus(const sigset_t *set, int sig)
{
return (set->__bits[_SIG_WORD(sig)] & _SIG_BIT(sig)) != 0;
}
/* Verify that sig is the lone bit set in the sigset. */
static void
siglonebit(const sigset_t *set, int sig)
{
int i;
for (i = 0; i < _SIG_WORDS; ++i) {
if (i != _SIG_WORD(sig))
ATF_REQUIRE_MSG(set->__bits[i] == 0,
"word %d altered to %x", i, set->__bits[i]);
else
ATF_REQUIRE_MSG((set->__bits[i] & ~_SIG_BIT(sig)) == 0,
"word %d has other bits set: %x", i,
set->__bits[i] & ~_SIG_BIT(sig));
}
}
static void
sigcompare(const sigset_t *left, const sigset_t *right)
{
int i;
for (i = 0; i < _SIG_WORDS; ++i) {
ATF_REQUIRE_MSG(left->__bits[i] == right->__bits[i],
"sig comparison failed at %d; left=%x, right=%x",
i, left->__bits[i], right->__bits[i]);
}
}
/*
* Test implementation details of our sigsetops... make sure the correct bits
* are getting set, for the most part, and that duplicate operations don't
* error out.
*/
ATF_TC_WITHOUT_HEAD(posix_sigsetop_test);
ATF_TC_BODY(posix_sigsetop_test, tc)
{
sigset_t set;
int i;
ATF_REQUIRE(sigfillset(&set) == 0);
for (i = 0; i < _SIG_WORDS; ++i) {
ATF_REQUIRE_MSG(set.__bits[i] == ~0U, "sigfillset failed @ %d",
i);
}
ATF_REQUIRE(sigemptyset(&set) == 0);
for (i = 0; i < _SIG_WORDS; ++i) {
ATF_REQUIRE_MSG(set.__bits[i] == 0, "sigemptyset failed @ %d",
i);
}
/* Ensure that sigismember reflects the empty set status. */
for (i = 1; i < NSIG; ++i) {
ATF_REQUIRE(sigismember(&set, i) == 0);
}
ATF_REQUIRE(sigaddset(&set, -1) == -1 && errno == EINVAL);
ATF_REQUIRE(sigaddset(&set, _SIG_MAXSIG + 1) == -1 && errno == EINVAL);
ATF_REQUIRE(sigdelset(&set, -1) == -1 && errno == EINVAL);
ATF_REQUIRE(sigdelset(&set, _SIG_MAXSIG + 1) == -1 && errno == EINVAL);
ATF_REQUIRE(sigaddset(&set, SIGSEGV) == 0);
ATF_REQUIRE(sigismember(&set, SIGSEGV) != 0);
ATF_REQUIRE_MSG(sigbitstatus(&set, SIGSEGV), "sigaddset failure");
siglonebit(&set, SIGSEGV);
/*
* A second addition should succeed without altering the state. This
* should be trivially true.
*/
ATF_REQUIRE(sigaddset(&set, SIGSEGV) == 0);
ATF_REQUIRE_MSG(sigbitstatus(&set, SIGSEGV),
"sigaddset twice changed bit");
ATF_REQUIRE(sigdelset(&set, SIGSEGV) == 0);
ATF_REQUIRE_MSG(!sigbitstatus(&set, SIGSEGV), "sigdelset failure");
ATF_REQUIRE(sigismember(&set, SIGSEGV) == 0);
ATF_REQUIRE(sigdelset(&set, SIGSEGV) == 0);
ATF_REQUIRE_MSG(!sigbitstatus(&set, SIGSEGV),
"sigdelset twice changed bit");
for (i = 0; i < _SIG_WORDS; ++i) {
ATF_REQUIRE_MSG(set.__bits[i] == 0, "set not empty @ %d",
i);
}
for (i = 1; i < NSIG; ++i) {
ATF_REQUIRE(sigismember(&set, i) == 0);
}
}
/*
* Test extended sigset ops for union/intersection and testing of empty set.
*/
ATF_TC_WITHOUT_HEAD(extended_sigsetop_test);
ATF_TC_BODY(extended_sigsetop_test, tc)
{
sigset_t chkset, set1, set2, set3;
sigemptyset(&chkset);
sigemptyset(&set1);
sigemptyset(&set2);
ATF_REQUIRE(sigisemptyset(&chkset) != 0);
sigaddset(&set1, SIGSEGV);
sigaddset(&set2, SIGKILL);
sigaddset(&chkset, SIGSEGV);
ATF_REQUIRE(sigisemptyset(&chkset) == 0);
sigaddset(&chkset, SIGKILL);
ATF_REQUIRE(sigorset(&set3, &set1, &set2) == 0);
ATF_REQUIRE(sigbitstatus(&set3, SIGSEGV));
ATF_REQUIRE(sigbitstatus(&set3, SIGKILL));
/*
* chkset was built with our POSIX-specified set operations that we've
* already tested, so it's a good comparison.
*/
sigcompare(&chkset, &set3);
/*
* Clear chkset; make sure sigisemptyset() still looks ok. sigaddset
* and sigdelset have already been tested to make sure that they're not
* touching other bits.
*/
sigdelset(&chkset, SIGSEGV);
sigdelset(&chkset, SIGKILL);
ATF_REQUIRE(sigisemptyset(&chkset) != 0);
ATF_REQUIRE(sigandset(&set3, &set1, &set2) == 0);
/* Make sure we clobbered these. */
ATF_REQUIRE(!sigbitstatus(&set3, SIGSEGV));
ATF_REQUIRE(!sigbitstatus(&set3, SIGKILL));
ATF_REQUIRE(sigisemptyset(&set3) != 0);
/* Rebuild for sigandset test */
sigemptyset(&set1);
sigemptyset(&set2);
sigaddset(&set1, SIGSEGV);
sigaddset(&set1, SIGKILL);
sigaddset(&set2, SIGSEGV);
ATF_REQUIRE(sigandset(&set3, &set1, &set2) == 0);
ATF_REQUIRE(sigbitstatus(&set3, SIGSEGV));
ATF_REQUIRE(!sigbitstatus(&set3, SIGKILL));
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, posix_sigsetop_test);
ATF_TP_ADD_TC(tp, extended_sigsetop_test);
return (atf_no_error());
}