Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2010 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 #include "net/base/network_change_notifier_linux.h"
      6 
      7 #include <errno.h>
      8 #include <sys/socket.h>
      9 
     10 #include "base/compiler_specific.h"
     11 #include "base/eintr_wrapper.h"
     12 #include "base/task.h"
     13 #include "base/threading/thread.h"
     14 #include "net/base/net_errors.h"
     15 #include "net/base/network_change_notifier_netlink_linux.h"
     16 
     17 namespace net {
     18 
     19 namespace {
     20 
     21 const int kInvalidSocket = -1;
     22 
     23 }  // namespace
     24 
     25 class NetworkChangeNotifierLinux::Thread
     26     : public base::Thread, public MessageLoopForIO::Watcher {
     27  public:
     28   Thread();
     29   virtual ~Thread();
     30 
     31   // MessageLoopForIO::Watcher:
     32   virtual void OnFileCanReadWithoutBlocking(int fd);
     33   virtual void OnFileCanWriteWithoutBlocking(int /* fd */);
     34 
     35  protected:
     36   // base::Thread
     37   virtual void Init();
     38   virtual void CleanUp();
     39 
     40  private:
     41   void NotifyObserversOfIPAddressChange() {
     42     NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
     43   }
     44 
     45   // Starts listening for netlink messages.  Also handles the messages if there
     46   // are any available on the netlink socket.
     47   void ListenForNotifications();
     48 
     49   // Attempts to read from the netlink socket into |buf| of length |len|.
     50   // Returns the bytes read on synchronous success and ERR_IO_PENDING if the
     51   // recv() would block.  Otherwise, it returns a net error code.
     52   int ReadNotificationMessage(char* buf, size_t len);
     53 
     54   // The netlink socket descriptor.
     55   int netlink_fd_;
     56   MessageLoopForIO::FileDescriptorWatcher netlink_watcher_;
     57 
     58   // Technically only needed for ChromeOS, but it's ugly to #ifdef out.
     59   ScopedRunnableMethodFactory<Thread> method_factory_;
     60 
     61   DISALLOW_COPY_AND_ASSIGN(Thread);
     62 };
     63 
     64 NetworkChangeNotifierLinux::Thread::Thread()
     65     : base::Thread("NetworkChangeNotifier"),
     66       netlink_fd_(kInvalidSocket),
     67       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {}
     68 
     69 NetworkChangeNotifierLinux::Thread::~Thread() {}
     70 
     71 void NetworkChangeNotifierLinux::Thread::Init() {
     72   netlink_fd_ = InitializeNetlinkSocket();
     73   if (netlink_fd_ < 0) {
     74     netlink_fd_ = kInvalidSocket;
     75     return;
     76   }
     77   ListenForNotifications();
     78 }
     79 
     80 void NetworkChangeNotifierLinux::Thread::CleanUp() {
     81   if (netlink_fd_ != kInvalidSocket) {
     82     if (HANDLE_EINTR(close(netlink_fd_)) != 0)
     83       PLOG(ERROR) << "Failed to close socket";
     84     netlink_fd_ = kInvalidSocket;
     85     netlink_watcher_.StopWatchingFileDescriptor();
     86   }
     87 }
     88 
     89 void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) {
     90   DCHECK_EQ(fd, netlink_fd_);
     91   ListenForNotifications();
     92 }
     93 
     94 void NetworkChangeNotifierLinux::Thread::OnFileCanWriteWithoutBlocking(
     95     int /* fd */) {
     96   NOTREACHED();
     97 }
     98 
     99 void NetworkChangeNotifierLinux::Thread::ListenForNotifications() {
    100   char buf[4096];
    101   int rv = ReadNotificationMessage(buf, arraysize(buf));
    102   while (rv > 0) {
    103     if (HandleNetlinkMessage(buf, rv)) {
    104       VLOG(1) << "Detected IP address changes.";
    105 #if defined(OS_CHROMEOS)
    106       // TODO(oshima): chromium-os:8285 - introduced artificial delay to
    107       // work around the issue of network load issue after connection
    108       // restored. See the bug for more details.
    109       //  This should be removed once this bug is properly fixed.
    110       const int kObserverNotificationDelayMS = 200;
    111       message_loop()->PostDelayedTask(
    112           FROM_HERE,
    113           method_factory_.NewRunnableMethod(
    114               &Thread::NotifyObserversOfIPAddressChange),
    115           kObserverNotificationDelayMS);
    116 #else
    117       NotifyObserversOfIPAddressChange();
    118 #endif
    119     }
    120     rv = ReadNotificationMessage(buf, arraysize(buf));
    121   }
    122 
    123   if (rv == ERR_IO_PENDING) {
    124     rv = MessageLoopForIO::current()->WatchFileDescriptor(netlink_fd_, false,
    125         MessageLoopForIO::WATCH_READ, &netlink_watcher_, this);
    126     LOG_IF(ERROR, !rv) << "Failed to watch netlink socket: " << netlink_fd_;
    127   }
    128 }
    129 
    130 int NetworkChangeNotifierLinux::Thread::ReadNotificationMessage(
    131     char* buf,
    132     size_t len) {
    133   DCHECK_NE(len, 0u);
    134   DCHECK(buf);
    135   memset(buf, 0, sizeof(buf));
    136   int rv = recv(netlink_fd_, buf, len, 0);
    137   if (rv > 0)
    138     return rv;
    139 
    140   DCHECK_NE(rv, 0);
    141   if (errno != EAGAIN && errno != EWOULDBLOCK) {
    142     PLOG(DFATAL) << "recv";
    143     return ERR_FAILED;
    144   }
    145 
    146   return ERR_IO_PENDING;
    147 }
    148 
    149 NetworkChangeNotifierLinux::NetworkChangeNotifierLinux()
    150     : notifier_thread_(new Thread) {
    151   // We create this notifier thread because the notification implementation
    152   // needs a MessageLoopForIO, and there's no guarantee that
    153   // MessageLoop::current() meets that criterion.
    154   base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0);
    155   notifier_thread_->StartWithOptions(thread_options);
    156 }
    157 
    158 NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() {
    159   // We don't need to explicitly Stop(), but doing so allows us to sanity-
    160   // check that the notifier thread shut down properly.
    161   notifier_thread_->Stop();
    162 }
    163 
    164 bool NetworkChangeNotifierLinux::IsCurrentlyOffline() const {
    165   // TODO(eroman): http://crbug.com/53473
    166   return false;
    167 }
    168 
    169 }  // namespace net
    170