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 17namespace net { 18 19namespace { 20 21const int kInvalidSocket = -1; 22 23} // namespace 24 25class 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 64NetworkChangeNotifierLinux::Thread::Thread() 65 : base::Thread("NetworkChangeNotifier"), 66 netlink_fd_(kInvalidSocket), 67 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {} 68 69NetworkChangeNotifierLinux::Thread::~Thread() {} 70 71void NetworkChangeNotifierLinux::Thread::Init() { 72 netlink_fd_ = InitializeNetlinkSocket(); 73 if (netlink_fd_ < 0) { 74 netlink_fd_ = kInvalidSocket; 75 return; 76 } 77 ListenForNotifications(); 78} 79 80void 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 89void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) { 90 DCHECK_EQ(fd, netlink_fd_); 91 ListenForNotifications(); 92} 93 94void NetworkChangeNotifierLinux::Thread::OnFileCanWriteWithoutBlocking( 95 int /* fd */) { 96 NOTREACHED(); 97} 98 99void 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 130int 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 149NetworkChangeNotifierLinux::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 158NetworkChangeNotifierLinux::~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 164bool NetworkChangeNotifierLinux::IsCurrentlyOffline() const { 165 // TODO(eroman): http://crbug.com/53473 166 return false; 167} 168 169} // namespace net 170