Merge "Add SysV signal handling"
diff --git a/libc/Android.mk b/libc/Android.mk
index e9ee7fc..4cef97a 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -174,12 +174,17 @@
     bionic/sigdelset.cpp \
     bionic/sigemptyset.cpp \
     bionic/sigfillset.cpp \
+    bionic/sighold.cpp \
+    bionic/sigignore.cpp \
     bionic/sigismember.cpp \
     bionic/signal.cpp \
     bionic/signalfd.cpp \
+    bionic/sigpause.cpp \
     bionic/sigpending.cpp \
     bionic/sigprocmask.cpp \
     bionic/sigqueue.cpp \
+    bionic/sigrelse.cpp \
+    bionic/sigset.cpp \
     bionic/sigsuspend.cpp \
     bionic/sigtimedwait.cpp \
     bionic/sigwait.cpp \
diff --git a/libc/bionic/sighold.cpp b/libc/bionic/sighold.cpp
new file mode 100644
index 0000000..e9c8ca1
--- /dev/null
+++ b/libc/bionic/sighold.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <signal.h>
+
+int sighold(int sig) {
+  sigset_t set;
+  if (sigemptyset(&set) == -1) return -1;
+  if (sigaddset(&set, sig) == -1) return -1;
+  return sigprocmask(SIG_BLOCK, &set, nullptr);
+}
diff --git a/libc/bionic/sigignore.cpp b/libc/bionic/sigignore.cpp
new file mode 100644
index 0000000..06f458e
--- /dev/null
+++ b/libc/bionic/sigignore.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <signal.h>
+#include <string.h>
+
+int sigignore(int sig) {
+  struct sigaction sa;
+  memset(&sa, 0, sizeof(sa));
+  if (sigemptyset(&sa.sa_mask) == -1) return -1;
+  sa.sa_handler = SIG_IGN;
+  return sigaction(sig, &sa, nullptr);
+}
diff --git a/libc/bionic/sigpause.cpp b/libc/bionic/sigpause.cpp
new file mode 100644
index 0000000..8ba42d4
--- /dev/null
+++ b/libc/bionic/sigpause.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <signal.h>
+
+int sigpause(int sig) {
+  sigset_t set;
+  if (sigprocmask(SIG_SETMASK, nullptr, &set) == -1) return -1;
+  if (sigdelset(&set, sig) == -1) return -1;
+  return sigsuspend(&set);
+}
diff --git a/libc/bionic/sigrelse.cpp b/libc/bionic/sigrelse.cpp
new file mode 100644
index 0000000..ab5554e
--- /dev/null
+++ b/libc/bionic/sigrelse.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <signal.h>
+
+int sigrelse(int sig) {
+  sigset_t set;
+  if (sigemptyset(&set) == -1) return -1;
+  if (sigaddset(&set, sig) == -1) return -1;
+  return sigprocmask(SIG_UNBLOCK, &set, nullptr);
+}
diff --git a/libc/bionic/sigset.cpp b/libc/bionic/sigset.cpp
new file mode 100644
index 0000000..e3f3e72
--- /dev/null
+++ b/libc/bionic/sigset.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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 COPYRIGHT HOLDERS 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
+ * COPYRIGHT OWNER 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 <signal.h>
+#include <string.h>
+
+sighandler_t sigset(int sig, sighandler_t disp) {
+  struct sigaction new_sa;
+  if (disp != SIG_HOLD) {
+    memset(&new_sa, 0, sizeof(new_sa));
+    new_sa.sa_handler = disp;
+    sigemptyset(&new_sa.sa_mask);
+  }
+
+  struct sigaction old_sa;
+  if (sigaction(sig, disp == SIG_HOLD ? nullptr : &new_sa, &old_sa) == -1) {
+    return SIG_ERR;
+  }
+
+  sigset_t new_proc_mask;
+  sigemptyset(&new_proc_mask);
+  sigaddset(&new_proc_mask, sig);
+
+  sigset_t old_proc_mask;
+  if (sigprocmask(disp == SIG_HOLD ? SIG_BLOCK : SIG_UNBLOCK,
+                  &new_proc_mask, &old_proc_mask) == -1) {
+    return SIG_ERR;
+  }
+
+  return sigismember(&old_proc_mask, sig) ? SIG_HOLD : old_sa.sa_handler;
+}
diff --git a/libc/include/signal.h b/libc/include/signal.h
index 85c46c0..763bae9 100644
--- a/libc/include/signal.h
+++ b/libc/include/signal.h
@@ -64,6 +64,11 @@
 #define _NSIG (_KERNEL__NSIG + 1)
 #define NSIG _NSIG
 
+/* The kernel headers define SIG_DFL (0) and SIG_IGN (1) but not SIG_HOLD, since
+ * SIG_HOLD is only used by the deprecated SysV signal API.
+ */
+#define SIG_HOLD ((sighandler_t)(uintptr_t)2)
+
 /* We take a few real-time signals for ourselves. May as well use the same names as glibc. */
 #define SIGRTMIN (__libc_current_sigrtmin())
 #define SIGRTMAX (__libc_current_sigrtmax())
@@ -120,6 +125,12 @@
 extern int sigsuspend(const sigset_t*) __nonnull((1));
 extern int sigwait(const sigset_t*, int*) __nonnull((1, 2));
 
+extern int sighold(int) __attribute__((deprecated("use sigprocmask() or pthread_sigmask() instead")));
+extern int sigignore(int) __attribute__((deprecated("use sigaction() instead")));
+extern int sigpause(int) __attribute__((deprecated("use sigsuspend() instead")));
+extern int sigrelse(int) __attribute__((deprecated("use sigprocmask() or pthread_sigmask() instead")));
+extern sighandler_t sigset(int, sighandler_t) __attribute__((deprecated("use sigaction() instead")));
+
 extern int raise(int);
 extern int kill(pid_t, int);
 extern int killpg(int, int);
diff --git a/libc/libc.arm.brillo.map b/libc/libc.arm.brillo.map
index b8cdd3c..054b9be 100644
--- a/libc/libc.arm.brillo.map
+++ b/libc/libc.arm.brillo.map
@@ -1277,6 +1277,11 @@
     hasmntopt;
     pthread_getname_np;
     setdomainname;
+    sighold;
+    sigignore;
+    sigpause;
+    sigrelse;
+    sigset;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 57060ae..6177627 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1277,6 +1277,11 @@
     hasmntopt;
     pthread_getname_np;
     setdomainname;
+    sighold;
+    sigignore;
+    sigpause;
+    sigrelse;
+    sigset;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index ac9d009..15d6764 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1199,6 +1199,11 @@
     hasmntopt;
     pthread_getname_np;
     setdomainname;
+    sighold;
+    sigignore;
+    sigpause;
+    sigrelse;
+    sigset;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 9d51b8d..7332c08 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1303,6 +1303,11 @@
     hasmntopt;
     pthread_getname_np;
     setdomainname;
+    sighold;
+    sigignore;
+    sigpause;
+    sigrelse;
+    sigset;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.mips.brillo.map b/libc/libc.mips.brillo.map
index 7133f4b..5af070e 100644
--- a/libc/libc.mips.brillo.map
+++ b/libc/libc.mips.brillo.map
@@ -1261,6 +1261,11 @@
     hasmntopt;
     pthread_getname_np;
     setdomainname;
+    sighold;
+    sigignore;
+    sigpause;
+    sigrelse;
+    sigset;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index e9b15c3..98f1a29 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1261,6 +1261,11 @@
     hasmntopt;
     pthread_getname_np;
     setdomainname;
+    sighold;
+    sigignore;
+    sigpause;
+    sigrelse;
+    sigset;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index ac9d009..15d6764 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1199,6 +1199,11 @@
     hasmntopt;
     pthread_getname_np;
     setdomainname;
+    sighold;
+    sigignore;
+    sigpause;
+    sigrelse;
+    sigset;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.x86.brillo.map b/libc/libc.x86.brillo.map
index c67f533..5459b0b 100644
--- a/libc/libc.x86.brillo.map
+++ b/libc/libc.x86.brillo.map
@@ -1260,6 +1260,11 @@
     hasmntopt;
     pthread_getname_np;
     setdomainname;
+    sighold;
+    sigignore;
+    sigpause;
+    sigrelse;
+    sigset;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 6838184..050f047 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1260,6 +1260,11 @@
     hasmntopt;
     pthread_getname_np;
     setdomainname;
+    sighold;
+    sigignore;
+    sigpause;
+    sigrelse;
+    sigset;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index ac9d009..15d6764 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1199,6 +1199,11 @@
     hasmntopt;
     pthread_getname_np;
     setdomainname;
+    sighold;
+    sigignore;
+    sigpause;
+    sigrelse;
+    sigset;
 } LIBC_N;
 
 LIBC_PRIVATE {
diff --git a/tests/Android.mk b/tests/Android.mk
index aeb10ea..7fdf2f4 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -38,6 +38,10 @@
     -Werror \
     -fno-builtin \
 
+# We want to test deprecated API too.
+test_cflags += \
+    -Wno-deprecated-declarations \
+
 test_cflags += -D__STDC_LIMIT_MACROS  # For glibc.
 
 test_cppflags := \
diff --git a/tests/ScopedSignalHandler.h b/tests/ScopedSignalHandler.h
index 3fb60a1..8998d0d 100644
--- a/tests/ScopedSignalHandler.h
+++ b/tests/ScopedSignalHandler.h
@@ -39,6 +39,10 @@
     sigaction(signal_number_, &action_, &old_action_);
   }
 
+  ScopedSignalHandler(int signal_number) : signal_number_(signal_number) {
+    sigaction(signal_number, nullptr, &old_action_);
+  }
+
   ~ScopedSignalHandler() {
     sigaction(signal_number_, &old_action_, NULL);
   }
@@ -49,4 +53,18 @@
   const int signal_number_;
 };
 
+class ScopedSignalMask {
+ public:
+  ScopedSignalMask() {
+    sigprocmask(SIG_SETMASK, nullptr, &old_mask_);
+  }
+
+  ~ScopedSignalMask() {
+    sigprocmask(SIG_SETMASK, &old_mask_, nullptr);
+  }
+
+ private:
+  sigset_t old_mask_;
+};
+
 #endif // _BIONIC_TESTS_SCOPED_SIGNAL_HANDLER_H
diff --git a/tests/signal_test.cpp b/tests/signal_test.cpp
index c5128ea..36ac690 100644
--- a/tests/signal_test.cpp
+++ b/tests/signal_test.cpp
@@ -422,3 +422,108 @@
 
 #endif
 #endif
+
+TEST(signal, sigignore_EINVAL) {
+  errno = 0;
+  ASSERT_EQ(-1, sigignore(99999));
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(signal, sigignore) {
+  errno = 0;
+  EXPECT_EQ(-1, sigignore(SIGKILL));
+  EXPECT_EQ(errno, EINVAL);
+
+  errno = 0;
+  EXPECT_EQ(-1, sigignore(SIGSTOP));
+  EXPECT_EQ(errno, EINVAL);
+
+  ScopedSignalHandler sigalrm{SIGALRM};
+  ASSERT_EQ(0, sigignore(SIGALRM));
+
+  struct sigaction sa;
+  ASSERT_EQ(0, sigaction(SIGALRM, nullptr, &sa));
+  EXPECT_EQ(SIG_IGN, sa.sa_handler);
+}
+
+TEST(signal, sighold_EINVAL) {
+  errno = 0;
+  ASSERT_EQ(-1, sighold(99999));
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(signal, sigpause_EINVAL) {
+  errno = 0;
+  ASSERT_EQ(-1, sigpause(99999));
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(signal, sigrelse_EINVAL) {
+  errno = 0;
+  ASSERT_EQ(-1, sigpause(99999));
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(signal, sighold_sigpause_sigrelse) {
+  static int sigalrm_handler_call_count;
+  auto sigalrm_handler = [](int) { sigalrm_handler_call_count++; };
+  ScopedSignalHandler sigalrm{SIGALRM, sigalrm_handler};
+  ScopedSignalMask mask;
+  sigset_t set;
+
+  // sighold(SIGALRM) should add SIGALRM to the signal mask ...
+  ASSERT_EQ(0, sighold(SIGALRM));
+  ASSERT_EQ(0, sigprocmask(SIG_SETMASK, 0, &set));
+  EXPECT_TRUE(sigismember(&set, SIGALRM));
+
+  // ... preventing our SIGALRM handler from running ...
+  raise(SIGALRM);
+  ASSERT_EQ(0, sigalrm_handler_call_count);
+  // ... until sigpause(SIGALRM) temporarily unblocks it.
+  ASSERT_EQ(-1, sigpause(SIGALRM));
+  ASSERT_EQ(EINTR, errno);
+  ASSERT_EQ(1, sigalrm_handler_call_count);
+
+  // But sigpause(SIGALRM) shouldn't permanently unblock SIGALRM.
+  ASSERT_EQ(0, sigprocmask(SIG_SETMASK, 0, &set));
+  EXPECT_TRUE(sigismember(&set, SIGALRM));
+
+  ASSERT_EQ(0, sigrelse(SIGALRM));
+  ASSERT_EQ(0, sigprocmask(SIG_SETMASK, 0, &set));
+  EXPECT_FALSE(sigismember(&set, SIGALRM));
+}
+
+TEST(signal, sigset_EINVAL) {
+  errno = 0;
+  ASSERT_EQ(SIG_ERR, sigset(99999, SIG_DFL));
+  ASSERT_EQ(EINVAL, errno);
+}
+
+TEST(signal, sigset) {
+  auto sigalrm_handler = [](int) { };
+  ScopedSignalHandler sigalrm{SIGALRM, sigalrm_handler};
+  ScopedSignalMask mask;
+
+  // block SIGALRM so the next sigset(SIGARLM) call will return SIG_HOLD
+  sigset_t sigalrm_set;
+  sigemptyset(&sigalrm_set);
+  sigaddset(&sigalrm_set, SIGALRM);
+  ASSERT_EQ(0, sigprocmask(SIG_BLOCK, &sigalrm_set, nullptr));
+
+  sigset_t set;
+  ASSERT_EQ(SIG_HOLD, sigset(SIGALRM, sigalrm_handler));
+  ASSERT_EQ(0, sigprocmask(SIG_BLOCK, nullptr, &set));
+  EXPECT_FALSE(sigismember(&set, SIGALRM));
+
+  ASSERT_EQ(sigalrm_handler, sigset(SIGALRM, SIG_IGN));
+  ASSERT_EQ(0, sigprocmask(SIG_BLOCK, nullptr, &set));
+  EXPECT_FALSE(sigismember(&set, SIGALRM));
+
+  ASSERT_EQ(SIG_IGN, sigset(SIGALRM, SIG_DFL));
+  ASSERT_EQ(0, sigprocmask(SIG_BLOCK, nullptr, &set));
+  EXPECT_FALSE(sigismember(&set, SIGALRM));
+
+  ASSERT_EQ(SIG_DFL, sigset(SIGALRM, SIG_HOLD));
+  ASSERT_EQ(0, sigprocmask(SIG_BLOCK, nullptr, &set));
+  EXPECT_TRUE(sigismember(&set, SIGALRM));
+}