| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // TODO(jamiewalch): Add unit tests for this. |
| |
| #include "remoting/host/posix/signal_handler.h" |
| |
| #include <errno.h> |
| #include <signal.h> |
| |
| #include <list> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/compiler_specific.h" |
| #include "base/files/file_descriptor_watcher_posix.h" |
| #include "base/posix/eintr_wrapper.h" |
| #include "base/threading/platform_thread.h" |
| |
| namespace remoting { |
| namespace { |
| |
| int g_read_fd = 0; |
| int g_write_fd = 0; |
| |
| class SignalListener { |
| public: |
| SignalListener(); |
| |
| void AddSignalHandler(int signal, const SignalHandler& handler); |
| |
| void OnFileCanReadWithoutBlocking(); |
| |
| // Throughout the lifetime of this, OnFileCanReadWithoutBlocking() is called |
| // whenever data is available in |g_read_fd|. |
| std::unique_ptr<base::FileDescriptorWatcher::Controller> controller; |
| |
| private: |
| typedef std::pair<int, SignalHandler> SignalAndHandler; |
| typedef std::list<SignalAndHandler> SignalHandlers; |
| SignalHandlers signal_handlers_; |
| }; |
| |
| SignalListener::SignalListener() = default; |
| |
| void SignalListener::AddSignalHandler(int signal, |
| const SignalHandler& handler) { |
| signal_handlers_.push_back(SignalAndHandler(signal, handler)); |
| } |
| |
| void SignalListener::OnFileCanReadWithoutBlocking() { |
| char buffer; |
| int result = HANDLE_EINTR(read(g_read_fd, &buffer, sizeof(buffer))); |
| if (result > 0) { |
| for (SignalHandlers::const_iterator i = signal_handlers_.begin(); |
| i != signal_handlers_.end(); |
| ++i) { |
| if (i->first == buffer) { |
| i->second.Run(i->first); |
| } |
| } |
| } |
| } |
| |
| SignalListener* g_signal_listener = nullptr; |
| |
| void GlobalSignalHandler(int signal) { |
| char byte = signal; |
| ignore_result(write(g_write_fd, &byte, 1)); |
| } |
| |
| } // namespace |
| |
| // RegisterSignalHandler registers a signal handler that writes a byte to a |
| // pipe each time a signal is received. The read end of the pipe is registered |
| // with the current MessageLoop (which must be of type IO); whenever the pipe |
| // is readable, it invokes the specified callback. |
| // |
| // This arrangement is required because the set of system APIs that are safe to |
| // call from a signal handler is very limited (but does include write). |
| bool RegisterSignalHandler(int signal_number, const SignalHandler& handler) { |
| CHECK(signal_number < 256); // Don't want to worry about multi-byte writes. |
| if (!g_signal_listener) { |
| g_signal_listener = new SignalListener(); |
| } |
| if (!g_write_fd) { |
| int pipe_fd[2]; |
| int result = pipe(pipe_fd); |
| if (result < 0) { |
| LOG(ERROR) << "Could not create signal pipe: " << errno; |
| return false; |
| } |
| |
| g_read_fd = pipe_fd[0]; |
| g_write_fd = pipe_fd[1]; |
| |
| g_signal_listener->controller = base::FileDescriptorWatcher::WatchReadable( |
| g_read_fd, base::Bind(&SignalListener::OnFileCanReadWithoutBlocking, |
| base::Unretained(g_signal_listener))); |
| } |
| if (signal(signal_number, GlobalSignalHandler) == SIG_ERR) { |
| LOG(ERROR) << "signal() failed: " << errno; |
| return false; |
| } |
| g_signal_listener->AddSignalHandler(signal_number, handler); |
| return true; |
| } |
| |
| } // namespace remoting |