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