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 |