Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 1 | // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Alex Vakulenko | 9ed0cab | 2015-10-12 15:21:28 -0700 | [diff] [blame] | 5 | #include "brillo/asynchronous_signal_handler.h" |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 6 | |
| 7 | #include <signal.h> |
| 8 | #include <sys/types.h> |
| 9 | #include <unistd.h> |
| 10 | |
Alex Deymo | fd626d7 | 2015-07-20 17:27:51 -0700 | [diff] [blame] | 11 | #include <base/bind.h> |
Ben Chan | cb10464 | 2014-09-05 08:21:06 -0700 | [diff] [blame] | 12 | #include <base/files/file_util.h> |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 13 | #include <base/logging.h> |
| 14 | #include <base/message_loop/message_loop.h> |
Alex Deymo | fd626d7 | 2015-07-20 17:27:51 -0700 | [diff] [blame] | 15 | #include <base/posix/eintr_wrapper.h> |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 16 | |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 17 | namespace { |
| 18 | const int kInvalidDescriptor = -1; |
| 19 | } // namespace |
| 20 | |
Alex Vakulenko | 9ed0cab | 2015-10-12 15:21:28 -0700 | [diff] [blame] | 21 | namespace brillo { |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 22 | |
| 23 | AsynchronousSignalHandler::AsynchronousSignalHandler() |
Alex Deymo | fd626d7 | 2015-07-20 17:27:51 -0700 | [diff] [blame] | 24 | : descriptor_(kInvalidDescriptor) { |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 25 | CHECK_EQ(sigemptyset(&signal_mask_), 0) << "Failed to initialize signal mask"; |
| 26 | CHECK_EQ(sigemptyset(&saved_signal_mask_), 0) |
| 27 | << "Failed to initialize signal mask"; |
| 28 | } |
| 29 | |
| 30 | AsynchronousSignalHandler::~AsynchronousSignalHandler() { |
| 31 | if (descriptor_ != kInvalidDescriptor) { |
Alex Deymo | fd626d7 | 2015-07-20 17:27:51 -0700 | [diff] [blame] | 32 | MessageLoop::current()->CancelTask(fd_watcher_task_); |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 33 | |
Alex Deymo | fd626d7 | 2015-07-20 17:27:51 -0700 | [diff] [blame] | 34 | if (IGNORE_EINTR(close(descriptor_)) != 0) |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 35 | PLOG(WARNING) << "Failed to close file descriptor"; |
| 36 | |
| 37 | descriptor_ = kInvalidDescriptor; |
Alex Vakulenko | 9d99bc6 | 2014-08-28 13:42:01 -0700 | [diff] [blame] | 38 | CHECK_EQ(0, sigprocmask(SIG_SETMASK, &saved_signal_mask_, nullptr)); |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 39 | } |
| 40 | } |
| 41 | |
| 42 | void AsynchronousSignalHandler::Init() { |
| 43 | CHECK_EQ(kInvalidDescriptor, descriptor_); |
| 44 | CHECK_EQ(0, sigprocmask(SIG_BLOCK, &signal_mask_, &saved_signal_mask_)); |
| 45 | descriptor_ = |
| 46 | signalfd(descriptor_, &signal_mask_, SFD_CLOEXEC | SFD_NONBLOCK); |
| 47 | CHECK_NE(kInvalidDescriptor, descriptor_); |
Alex Deymo | fd626d7 | 2015-07-20 17:27:51 -0700 | [diff] [blame] | 48 | fd_watcher_task_ = MessageLoop::current()->WatchFileDescriptor( |
| 49 | FROM_HERE, |
Alex Vakulenko | 05d2904 | 2015-01-13 09:39:25 -0800 | [diff] [blame] | 50 | descriptor_, |
Alex Deymo | fd626d7 | 2015-07-20 17:27:51 -0700 | [diff] [blame] | 51 | MessageLoop::WatchMode::kWatchRead, |
Alex Vakulenko | 05d2904 | 2015-01-13 09:39:25 -0800 | [diff] [blame] | 52 | true, |
Alex Deymo | fd626d7 | 2015-07-20 17:27:51 -0700 | [diff] [blame] | 53 | base::Bind(&AsynchronousSignalHandler::OnFileCanReadWithoutBlocking, |
| 54 | base::Unretained(this))); |
| 55 | CHECK(fd_watcher_task_ != MessageLoop::kTaskIdNull) |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 56 | << "Watching shutdown pipe failed."; |
| 57 | } |
| 58 | |
| 59 | void AsynchronousSignalHandler::RegisterHandler(int signal, |
| 60 | const SignalHandler& callback) { |
| 61 | registered_callbacks_[signal] = callback; |
| 62 | CHECK_EQ(0, sigaddset(&signal_mask_, signal)); |
| 63 | UpdateSignals(); |
| 64 | } |
| 65 | |
| 66 | void AsynchronousSignalHandler::UnregisterHandler(int signal) { |
| 67 | Callbacks::iterator callback_it = registered_callbacks_.find(signal); |
| 68 | if (callback_it != registered_callbacks_.end()) { |
| 69 | registered_callbacks_.erase(callback_it); |
| 70 | ResetSignal(signal); |
| 71 | } |
| 72 | } |
| 73 | |
Alex Deymo | fd626d7 | 2015-07-20 17:27:51 -0700 | [diff] [blame] | 74 | void AsynchronousSignalHandler::OnFileCanReadWithoutBlocking() { |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 75 | struct signalfd_siginfo info; |
Alex Deymo | fd626d7 | 2015-07-20 17:27:51 -0700 | [diff] [blame] | 76 | while (base::ReadFromFD(descriptor_, |
| 77 | reinterpret_cast<char*>(&info), sizeof(info))) { |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 78 | int signal = info.ssi_signo; |
| 79 | Callbacks::iterator callback_it = registered_callbacks_.find(signal); |
| 80 | if (callback_it == registered_callbacks_.end()) { |
| 81 | LOG(WARNING) << "Unable to find a signal handler for signal: " << signal; |
| 82 | // Can happen if a signal has been called multiple time, and the callback |
| 83 | // asked to be unregistered the first time. |
| 84 | continue; |
| 85 | } |
| 86 | const SignalHandler& callback = callback_it->second; |
| 87 | bool must_unregister = callback.Run(info); |
| 88 | if (must_unregister) { |
| 89 | UnregisterHandler(signal); |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 94 | void AsynchronousSignalHandler::ResetSignal(int signal) { |
| 95 | CHECK_EQ(0, sigdelset(&signal_mask_, signal)); |
| 96 | UpdateSignals(); |
| 97 | } |
| 98 | |
| 99 | void AsynchronousSignalHandler::UpdateSignals() { |
| 100 | if (descriptor_ != kInvalidDescriptor) { |
Alex Vakulenko | 9d99bc6 | 2014-08-28 13:42:01 -0700 | [diff] [blame] | 101 | CHECK_EQ(0, sigprocmask(SIG_SETMASK, &saved_signal_mask_, nullptr)); |
| 102 | CHECK_EQ(0, sigprocmask(SIG_BLOCK, &signal_mask_, nullptr)); |
Benjamin Lerman | 21c7e37 | 2014-07-10 14:20:27 +0200 | [diff] [blame] | 103 | CHECK_EQ(descriptor_, |
| 104 | signalfd(descriptor_, &signal_mask_, SFD_CLOEXEC | SFD_NONBLOCK)); |
| 105 | } |
| 106 | } |
| 107 | |
Alex Vakulenko | 9ed0cab | 2015-10-12 15:21:28 -0700 | [diff] [blame] | 108 | } // namespace brillo |