Home | History | Annotate | Download | only in posix
      1 // Copyright (c) 2012 The Chromium 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 
      5 // TODO(jamiewalch): Add unit tests for this.
      6 
      7 #include "remoting/host/posix/signal_handler.h"
      8 
      9 #include <errno.h>
     10 #include <signal.h>
     11 
     12 #include <list>
     13 #include <utility>
     14 
     15 #include "base/compiler_specific.h"
     16 #include "base/message_loop/message_loop.h"
     17 #include "base/message_loop/message_pump_libevent.h"
     18 #include "base/posix/eintr_wrapper.h"
     19 #include "base/threading/platform_thread.h"
     20 
     21 namespace remoting {
     22 namespace {
     23 
     24 class SignalListener : public base::MessagePumpLibevent::Watcher {
     25  public:
     26   SignalListener();
     27 
     28   void AddSignalHandler(int signal, const SignalHandler& handler);
     29 
     30   virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
     31   virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {}
     32 
     33   // WatchFileDescriptor needs a controller through which the operation can be
     34   // canceled. We don't use it, but this is as good a place as any to store it.
     35   base::MessagePumpLibevent::FileDescriptorWatcher controller;
     36 
     37  private:
     38   typedef std::pair<int, SignalHandler> SignalAndHandler;
     39   typedef std::list<SignalAndHandler> SignalHandlers;
     40   SignalHandlers signal_handlers_;
     41 };
     42 
     43 SignalListener::SignalListener() {
     44 }
     45 
     46 void SignalListener::AddSignalHandler(int signal,
     47                                       const SignalHandler& handler) {
     48   signal_handlers_.push_back(SignalAndHandler(signal, handler));
     49 }
     50 
     51 void SignalListener::OnFileCanReadWithoutBlocking(int fd) {
     52   char buffer;
     53   int result = HANDLE_EINTR(read(fd, &buffer, sizeof(buffer)));
     54   if (result > 0) {
     55     for (SignalHandlers::const_iterator i = signal_handlers_.begin();
     56          i != signal_handlers_.end();
     57          ++i) {
     58       if (i->first == buffer) {
     59         i->second.Run(i->first);
     60       }
     61     }
     62   }
     63 }
     64 
     65 SignalListener* g_signal_listener = NULL;
     66 int g_write_fd = 0;
     67 
     68 void GlobalSignalHandler(int signal) {
     69   char byte = signal;
     70   int r ALLOW_UNUSED = write(g_write_fd, &byte, 1);
     71 }
     72 
     73 }  // namespace
     74 
     75 // RegisterSignalHandler registers a signal handler that writes a byte to a
     76 // pipe each time a signal is received. The read end of the pipe is registered
     77 // with the current MessageLoop (which must be of type IO); whenever the pipe
     78 // is readable, it invokes the specified callback.
     79 //
     80 // This arrangement is required because the set of system APIs that are safe to
     81 // call from a signal handler is very limited (but does include write).
     82 bool RegisterSignalHandler(int signal_number, const SignalHandler& handler) {
     83   CHECK(signal_number < 256);  // Don't want to worry about multi-byte writes.
     84   if (!g_signal_listener) {
     85     g_signal_listener = new SignalListener();
     86   }
     87   if (!g_write_fd) {
     88     int pipe_fd[2];
     89     int result = pipe(pipe_fd);
     90     if (result < 0) {
     91       LOG(ERROR) << "Could not create signal pipe: " << errno;
     92       return false;
     93     }
     94     base::MessageLoopForIO* message_loop = base::MessageLoopForIO::current();
     95     result =
     96         message_loop->WatchFileDescriptor(pipe_fd[0],
     97                                           true,
     98                                           base::MessageLoopForIO::WATCH_READ,
     99                                           &g_signal_listener->controller,
    100                                           g_signal_listener);
    101     if (!result) {
    102       LOG(ERROR) << "Failed to create signal detector task.";
    103       close(pipe_fd[0]);
    104       close(pipe_fd[1]);
    105       return false;
    106     }
    107     g_write_fd = pipe_fd[1];
    108   }
    109   if (signal(signal_number, GlobalSignalHandler) == SIG_ERR) {
    110     LOG(ERROR) << "signal() failed: " << errno;
    111     return false;
    112   }
    113   g_signal_listener->AddSignalHandler(signal_number, handler);
    114   return true;
    115 }
    116 
    117 }  // namespace remoting
    118