| // Copyright 2023 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| #ifndef NINJA_INTERRUPT_HANDLING_H_ |
| #define NINJA_INTERRUPT_HANDLING_H_ |
| |
| /// Convenience classes used to control how user interruption |
| /// is handled by Ninja at various times. This means Ctrl+C and Ctrl+Break |
| /// events on Win32, and SIGINT/SIGHUP/SIGTERM ones on Posix. |
| |
| #ifdef _WIN32 |
| |
| #include <windows.h> |
| |
| /// On Ctrl-C or Ctrl-Break, post en empty i/o completion packet |
| /// to a given i/o completion queue. Useful to catch signals when |
| /// waiting for overlapped i/o on Win32. |
| struct InterruptCompletionPortHandler { |
| InterruptCompletionPortHandler(HANDLE ioport, ULONG_PTR completion_key = 0); |
| ~InterruptCompletionPortHandler(); |
| |
| private: |
| static BOOL WINAPI HandlerRoutine(DWORD dwCtrlType); |
| HANDLE old_ioport_; |
| ULONG_PTR old_completion_key_; |
| static HANDLE s_ioport_; |
| static ULONG_PTR s_completion_key_; |
| }; |
| |
| /// Forward all Ctrl-C and Ctrl-Break signals to a different |
| /// process group. |
| struct InterruptForwarder { |
| InterruptForwarder(DWORD process_group_id); |
| ~InterruptForwarder(); |
| |
| private: |
| static BOOL WINAPI HandlerRoutine(DWORD dwCtrlType); |
| DWORD old_process_group_id_; |
| static DWORD s_process_group_id_; |
| }; |
| |
| #else // !_WIN32 |
| |
| #include <signal.h> |
| |
| /// A class used to block all SIGINT/SIGHUP/SIGTERM signals |
| /// in the current process. Restores the previous signal |
| /// mask in the destructor. |
| struct InterruptBlocker { |
| InterruptBlocker(); |
| ~InterruptBlocker(); |
| |
| const sigset_t& old_mask() const { return prev_signal_mask_; } |
| |
| private: |
| sigset_t prev_signal_mask_; |
| }; |
| |
| /// Base class for all interrupt handlers. |
| struct InterruptHandlerBase { |
| /// Constructor sets a new signal handler for SIGINT/SIGHUP/SIGTERM |
| /// and unblocks these signals! |
| InterruptHandlerBase(const struct sigaction& action); |
| |
| /// Destructor restores previous interrupt handlers and signal mask. |
| ~InterruptHandlerBase(); |
| |
| /// Return the signal mask before construction. This will be restored |
| /// on destruction as well. |
| sigset_t old_mask() const { return old_mask_; } |
| |
| private: |
| struct sigaction old_int_action_; |
| struct sigaction old_hup_action_; |
| struct sigaction old_term_action_; |
| sigset_t old_mask_; |
| }; |
| |
| /// Catch all SIGINT/SIGHUP/SIGTERM signals and stores the |
| /// corresponding signal number into a global interrupted() |
| /// variable. This can also detect pending signals. |
| struct InterruptCatcher : public InterruptHandlerBase { |
| InterruptCatcher(); |
| ~InterruptCatcher(); |
| |
| /// Return interrupt signal number, or 0 if there were none. |
| int interrupted() const { return s_interrupted_; } |
| |
| /// Clear the interrupted signal number. |
| void Clear() { s_interrupted_ = 0; } |
| |
| /// Handle any pending interruption signal. This updates |
| /// the interrupted() value but does not cancel the signals. |
| static void HandlePendingInterrupt(); |
| |
| private: |
| static struct sigaction MakeAction(); |
| static volatile sig_atomic_t s_interrupted_; |
| }; |
| |
| /// Forward all SIGINT/SIGHUP/SIGTERM signal to a different |
| /// process group |
| struct InterruptForwarder : public InterruptHandlerBase { |
| explicit InterruptForwarder(pid_t process_group); |
| ~InterruptForwarder(); |
| |
| private: |
| static struct sigaction MakeAction(); |
| pid_t old_process_group_ = -1; |
| static pid_t s_process_group_; |
| }; |
| |
| #endif // !_WIN32 |
| |
| #endif // NINJA_INTERRUPT_HANDLING_H_ |