network_change_notifier_linux.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
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/eintr_wrapper.h" 11#include "base/task.h" 12#include "base/thread.h" 13#include "net/base/net_errors.h" 14#include "net/base/network_change_notifier_netlink_linux.h" 15 16// We only post tasks to a child thread we own, so we don't need refcounting. 17DISABLE_RUNNABLE_METHOD_REFCOUNT(net::NetworkChangeNotifierLinux); 18 19namespace net { 20 21namespace { 22 23const int kInvalidSocket = -1; 24 25} // namespace 26 27NetworkChangeNotifierLinux::NetworkChangeNotifierLinux() 28 : notifier_thread_(new base::Thread("NetworkChangeNotifier")), 29 netlink_fd_(kInvalidSocket) { 30 // We create this notifier thread because the notification implementation 31 // needs a MessageLoopForIO, and there's no guarantee that 32 // MessageLoop::current() meets that criterion. 33 base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0); 34 notifier_thread_->StartWithOptions(thread_options); 35 notifier_thread_->message_loop()->PostTask(FROM_HERE, 36 NewRunnableMethod(this, &NetworkChangeNotifierLinux::Init)); 37} 38 39NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() { 40 // We don't need to explicitly Stop(), but doing so allows us to sanity- 41 // check that the notifier thread shut down properly. 42 notifier_thread_->Stop(); 43 DCHECK_EQ(kInvalidSocket, netlink_fd_); 44} 45 46void NetworkChangeNotifierLinux::WillDestroyCurrentMessageLoop() { 47 DCHECK(notifier_thread_ != NULL); 48 // We can't check the notifier_thread_'s message_loop(), as it's now 0. 49 // DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); 50 51 if (netlink_fd_ != kInvalidSocket) { 52 if (HANDLE_EINTR(close(netlink_fd_)) != 0) 53 PLOG(ERROR) << "Failed to close socket"; 54 netlink_fd_ = kInvalidSocket; 55 netlink_watcher_.StopWatchingFileDescriptor(); 56 } 57} 58 59void NetworkChangeNotifierLinux::OnFileCanReadWithoutBlocking(int fd) { 60 DCHECK(notifier_thread_ != NULL); 61 DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); 62 63 DCHECK_EQ(fd, netlink_fd_); 64 ListenForNotifications(); 65} 66 67void NetworkChangeNotifierLinux::OnFileCanWriteWithoutBlocking(int /* fd */) { 68 DCHECK(notifier_thread_ != NULL); 69 DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); 70 71 NOTREACHED(); 72} 73 74void NetworkChangeNotifierLinux::Init() { 75 DCHECK(notifier_thread_ != NULL); 76 DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); 77 78 netlink_fd_ = InitializeNetlinkSocket(); 79 if (netlink_fd_ < 0) { 80 netlink_fd_ = kInvalidSocket; 81 return; 82 } 83 MessageLoop::current()->AddDestructionObserver(this); 84 ListenForNotifications(); 85} 86 87void NetworkChangeNotifierLinux::ListenForNotifications() { 88 DCHECK(notifier_thread_ != NULL); 89 DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); 90 91 char buf[4096]; 92 int rv = ReadNotificationMessage(buf, arraysize(buf)); 93 while (rv > 0) { 94 if (HandleNetlinkMessage(buf, rv)) { 95 LOG(INFO) << "Detected IP address changes."; 96#if defined(OS_CHROMEOS) 97 // TODO(zelidrag): chromium-os:3996 - introduced artificial delay to 98 // work around the issue of proxy initialization before name resolving 99 // is functional in ChromeOS. This should be removed once this bug 100 // is properly fixed. 101 const int kObserverNotificationDelayMS = 500; 102 MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableFunction( 103 &NetworkChangeNotifier::NotifyObserversOfIPAddressChange), 104 kObserverNotificationDelayMS); 105#else 106 NotifyObserversOfIPAddressChange(); 107#endif 108 } 109 rv = ReadNotificationMessage(buf, arraysize(buf)); 110 } 111 112 if (rv == ERR_IO_PENDING) { 113 rv = MessageLoopForIO::current()->WatchFileDescriptor(netlink_fd_, false, 114 MessageLoopForIO::WATCH_READ, &netlink_watcher_, this); 115 LOG_IF(ERROR, !rv) << "Failed to watch netlink socket: " << netlink_fd_; 116 } 117} 118 119int NetworkChangeNotifierLinux::ReadNotificationMessage(char* buf, size_t len) { 120 DCHECK(notifier_thread_ != NULL); 121 DCHECK_EQ(notifier_thread_->message_loop(), MessageLoop::current()); 122 123 DCHECK_NE(len, 0u); 124 DCHECK(buf); 125 memset(buf, 0, sizeof(buf)); 126 int rv = recv(netlink_fd_, buf, len, 0); 127 if (rv > 0) 128 return rv; 129 130 DCHECK_NE(rv, 0); 131 if (errno != EAGAIN && errno != EWOULDBLOCK) { 132 PLOG(DFATAL) << "recv"; 133 return ERR_FAILED; 134 } 135 136 return ERR_IO_PENDING; 137} 138 139} // namespace net 140