| /* Self tests for scoped_ignored_signal for GDB, the GNU debugger. | 
 |  | 
 |    Copyright (C) 2021-2023 Free Software Foundation, Inc. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program 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 General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include "gdbsupport/scoped_ignore_signal.h" | 
 | #include "gdbsupport/selftest.h" | 
 | #include "gdbsupport/scope-exit.h" | 
 | #include <unistd.h> | 
 | #include <signal.h> | 
 |  | 
 | namespace selftests { | 
 | namespace scoped_ignore_sig { | 
 |  | 
 | #ifdef SIGPIPE | 
 |  | 
 | /* True if the SIGPIPE handler ran.  */ | 
 | static volatile sig_atomic_t got_sigpipe = 0; | 
 |  | 
 | /* SIGPIPE handler for testing.  */ | 
 |  | 
 | static void | 
 | handle_sigpipe (int) | 
 | { | 
 |   got_sigpipe = 1; | 
 | } | 
 |  | 
 | /* Test scoped_ignore_sigpipe.  */ | 
 |  | 
 | static void | 
 | test_sigpipe () | 
 | { | 
 |   auto *osig = signal (SIGPIPE, handle_sigpipe); | 
 |   SCOPE_EXIT { signal (SIGPIPE, osig); }; | 
 |  | 
 | #ifdef HAVE_SIGPROCMASK | 
 |   /* Make sure SIGPIPE isn't blocked.  */ | 
 |   sigset_t set, old_state; | 
 |   sigemptyset (&set); | 
 |   sigaddset (&set, SIGPIPE); | 
 |   sigprocmask (SIG_UNBLOCK, &set, &old_state); | 
 |   SCOPE_EXIT { sigprocmask (SIG_SETMASK, &old_state, nullptr); }; | 
 | #endif | 
 |  | 
 |   /* Create pipe, and close read end so that writes to the pipe fail | 
 |      with EPIPE.  */ | 
 |  | 
 |   int fd[2]; | 
 |   char c = 0xff; | 
 |   int r; | 
 |  | 
 |   r = pipe (fd); | 
 |   SELF_CHECK (r == 0); | 
 |  | 
 |   close (fd[0]); | 
 |   SCOPE_EXIT { close (fd[1]); }; | 
 |  | 
 |   /* Check that writing to the pipe results in EPIPE.  EXPECT_SIG | 
 |      indicates whether a SIGPIPE signal is expected.  */ | 
 |   auto check_pipe_write = [&] (bool expect_sig) | 
 |   { | 
 |     got_sigpipe = 0; | 
 |     errno = 0; | 
 |  | 
 |     r = write (fd[1], &c, 1); | 
 |     SELF_CHECK (r == -1 && errno == EPIPE | 
 | 		&& got_sigpipe == expect_sig); | 
 |   }; | 
 |  | 
 |   /* Check that without a scoped_ignore_sigpipe in scope we indeed get | 
 |      a SIGPIPE signal.  */ | 
 |   check_pipe_write (true); | 
 |  | 
 |   /* Now check that with a scoped_ignore_sigpipe in scope, SIGPIPE is | 
 |      ignored/blocked.  */ | 
 |   { | 
 |     scoped_ignore_sigpipe ignore1; | 
 |  | 
 |     check_pipe_write (false); | 
 |  | 
 |     /* Check that scoped_ignore_sigpipe nests correctly.  */ | 
 |     { | 
 |       scoped_ignore_sigpipe ignore2; | 
 |  | 
 |       check_pipe_write (false); | 
 |     } | 
 |  | 
 |     /* If nesting works correctly, this write results in no | 
 |        SIGPIPE.  */ | 
 |     check_pipe_write (false); | 
 |   } | 
 |  | 
 |   /* No scoped_ignore_sigpipe is in scope anymore, so this should | 
 |      result in a SIGPIPE signal.  */ | 
 |   check_pipe_write (true); | 
 | } | 
 |  | 
 | #endif /* SIGPIPE */ | 
 |  | 
 | } /* namespace scoped_ignore_sig */ | 
 | } /* namespace selftests */ | 
 |  | 
 | void _initialize_scoped_ignore_signal_selftests (); | 
 | void | 
 | _initialize_scoped_ignore_signal_selftests () | 
 | { | 
 | #ifdef SIGPIPE | 
 |   selftests::register_test ("scoped_ignore_sigpipe", | 
 | 			    selftests::scoped_ignore_sig::test_sigpipe); | 
 | #endif | 
 | } |