OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 // This implementation of NetworkChangeNotifier's offline state detection | 5 // This implementation of NetworkChangeNotifier's offline state detection |
6 // depends on D-Bus and NetworkManager, and is known to work on at least | 6 // depends on D-Bus and NetworkManager, and is known to work on at least |
7 // GNOME version 2.30. If D-Bus or NetworkManager are unavailable, this | 7 // GNOME version 2.30. If D-Bus or NetworkManager are unavailable, this |
8 // implementation will always behave as if it is online. | 8 // implementation will always behave as if it is online. |
9 | 9 |
10 #include "net/base/network_change_notifier_linux.h" | 10 #include "net/base/network_change_notifier_linux.h" |
11 | 11 |
12 #include <errno.h> | |
13 #include <resolv.h> | 12 #include <resolv.h> |
14 #include <sys/socket.h> | |
15 | 13 |
16 #include "base/bind.h" | 14 #include "base/bind.h" |
17 #include "base/bind_helpers.h" | 15 #include "base/bind_helpers.h" |
18 #include "base/callback.h" | 16 #include "base/callback.h" |
19 #include "base/compiler_specific.h" | 17 #include "base/compiler_specific.h" |
20 #include "base/eintr_wrapper.h" | |
21 #include "base/memory/weak_ptr.h" | 18 #include "base/memory/weak_ptr.h" |
22 #include "base/synchronization/lock.h" | 19 #include "base/synchronization/lock.h" |
23 #include "base/synchronization/waitable_event.h" | 20 #include "base/synchronization/waitable_event.h" |
24 #include "base/threading/platform_thread.h" | 21 #include "base/threading/platform_thread.h" |
25 #include "base/threading/thread.h" | 22 #include "base/threading/thread.h" |
26 #include "base/threading/thread_restrictions.h" | 23 #include "base/threading/thread_restrictions.h" |
27 #include "dbus/bus.h" | 24 #include "dbus/bus.h" |
28 #include "dbus/message.h" | 25 #include "dbus/message.h" |
29 #include "dbus/object_proxy.h" | 26 #include "dbus/object_proxy.h" |
| 27 #include "net/base/address_tracker_linux.h" |
30 #include "net/base/net_errors.h" | 28 #include "net/base/net_errors.h" |
31 #include "net/base/network_change_notifier_netlink_linux.h" | |
32 #include "net/dns/dns_config_watcher.h" | 29 #include "net/dns/dns_config_watcher.h" |
33 | 30 |
34 namespace net { | 31 namespace net { |
35 | 32 |
36 namespace { | 33 namespace { |
37 | 34 |
38 const int kInvalidSocket = -1; | |
39 | |
40 const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager"; | 35 const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager"; |
41 const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager"; | 36 const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager"; |
42 const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager"; | 37 const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager"; |
43 | 38 |
44 // http://projects.gnome.org/NetworkManager/developers/spec-08.html#type-NM_STAT
E | 39 // http://projects.gnome.org/NetworkManager/developers/spec-08.html#type-NM_STAT
E |
45 enum { | 40 enum { |
46 NM_LEGACY_STATE_UNKNOWN = 0, | 41 NM_LEGACY_STATE_UNKNOWN = 0, |
47 NM_LEGACY_STATE_ASLEEP = 1, | 42 NM_LEGACY_STATE_ASLEEP = 1, |
48 NM_LEGACY_STATE_CONNECTING = 2, | 43 NM_LEGACY_STATE_CONNECTING = 2, |
49 NM_LEGACY_STATE_CONNECTED = 3, | 44 NM_LEGACY_STATE_CONNECTED = 3, |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 NetworkManagerApi::GetCurrentConnectionType() { | 231 NetworkManagerApi::GetCurrentConnectionType() { |
237 // http://crbug.com/125097 | 232 // http://crbug.com/125097 |
238 base::ThreadRestrictions::ScopedAllowWait allow_wait; | 233 base::ThreadRestrictions::ScopedAllowWait allow_wait; |
239 offline_state_initialized_.Wait(); | 234 offline_state_initialized_.Wait(); |
240 base::AutoLock lock(is_offline_lock_); | 235 base::AutoLock lock(is_offline_lock_); |
241 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. | 236 // TODO(droger): Return something more detailed than CONNECTION_UNKNOWN. |
242 return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE : | 237 return is_offline_ ? NetworkChangeNotifier::CONNECTION_NONE : |
243 NetworkChangeNotifier::CONNECTION_UNKNOWN; | 238 NetworkChangeNotifier::CONNECTION_UNKNOWN; |
244 } | 239 } |
245 | 240 |
246 class NetworkChangeNotifierLinux::Thread | 241 class NetworkChangeNotifierLinux::Thread : public base::Thread { |
247 : public base::Thread, public MessageLoopForIO::Watcher { | |
248 public: | 242 public: |
249 explicit Thread(dbus::Bus* bus); | 243 explicit Thread(dbus::Bus* bus); |
250 virtual ~Thread(); | 244 virtual ~Thread(); |
251 | 245 |
252 // MessageLoopForIO::Watcher: | |
253 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; | |
254 virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE; | |
255 | |
256 // Plumbing for NetworkChangeNotifier::GetCurrentConnectionType. | 246 // Plumbing for NetworkChangeNotifier::GetCurrentConnectionType. |
257 // Safe to call from any thread. | 247 // Safe to call from any thread. |
258 NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() { | 248 NetworkChangeNotifier::ConnectionType GetCurrentConnectionType() { |
259 return network_manager_api_.GetCurrentConnectionType(); | 249 return network_manager_api_.GetCurrentConnectionType(); |
260 } | 250 } |
261 | 251 |
| 252 const internal::AddressTrackerLinux* address_tracker() const { |
| 253 return &address_tracker_; |
| 254 } |
| 255 |
262 protected: | 256 protected: |
263 // base::Thread | 257 // base::Thread |
264 virtual void Init() OVERRIDE; | 258 virtual void Init() OVERRIDE; |
265 virtual void CleanUp() OVERRIDE; | 259 virtual void CleanUp() OVERRIDE; |
266 | 260 |
267 private: | 261 private: |
268 // Starts listening for netlink messages. Also handles the messages if there | |
269 // are any available on the netlink socket. | |
270 void ListenForNotifications(); | |
271 | |
272 // Attempts to read from the netlink socket into |buf| of length |len|. | |
273 // Returns the bytes read on synchronous success and ERR_IO_PENDING if the | |
274 // recv() would block. Otherwise, it returns a net error code. | |
275 int ReadNotificationMessage(char* buf, size_t len); | |
276 | |
277 // The netlink socket descriptor. | |
278 int netlink_fd_; | |
279 MessageLoopForIO::FileDescriptorWatcher netlink_watcher_; | |
280 | |
281 // Used to detect online/offline state changes. | 262 // Used to detect online/offline state changes. |
282 NetworkManagerApi network_manager_api_; | 263 NetworkManagerApi network_manager_api_; |
283 | 264 |
284 internal::DnsConfigWatcher dns_watcher_; | 265 internal::DnsConfigWatcher dns_watcher_; |
| 266 internal::AddressTrackerLinux address_tracker_; |
285 | 267 |
286 DISALLOW_COPY_AND_ASSIGN(Thread); | 268 DISALLOW_COPY_AND_ASSIGN(Thread); |
287 }; | 269 }; |
288 | 270 |
289 NetworkChangeNotifierLinux::Thread::Thread(dbus::Bus* bus) | 271 NetworkChangeNotifierLinux::Thread::Thread(dbus::Bus* bus) |
290 : base::Thread("NetworkChangeNotifier"), | 272 : base::Thread("NetworkChangeNotifier"), |
291 netlink_fd_(kInvalidSocket), | |
292 network_manager_api_( | 273 network_manager_api_( |
293 base::Bind(&NetworkChangeNotifier:: | 274 base::Bind(&NetworkChangeNotifier:: |
294 NotifyObserversOfConnectionTypeChange), | 275 NotifyObserversOfConnectionTypeChange), |
295 bus) { | 276 bus), |
| 277 address_tracker_( |
| 278 base::Bind(&NetworkChangeNotifier:: |
| 279 NotifyObserversOfIPAddressChange)) { |
296 } | 280 } |
297 | 281 |
298 NetworkChangeNotifierLinux::Thread::~Thread() { | 282 NetworkChangeNotifierLinux::Thread::~Thread() { |
299 DCHECK(!Thread::IsRunning()); | 283 DCHECK(!Thread::IsRunning()); |
300 } | 284 } |
301 | 285 |
302 void NetworkChangeNotifierLinux::Thread::Init() { | 286 void NetworkChangeNotifierLinux::Thread::Init() { |
303 netlink_fd_ = InitializeNetlinkSocket(); | |
304 if (netlink_fd_ < 0) { | |
305 netlink_fd_ = kInvalidSocket; | |
306 return; | |
307 } | |
308 ListenForNotifications(); | |
309 | |
310 network_manager_api_.Init(); | 287 network_manager_api_.Init(); |
311 | |
312 dns_watcher_.Init(); | 288 dns_watcher_.Init(); |
| 289 address_tracker_.Init(); |
313 } | 290 } |
314 | 291 |
315 void NetworkChangeNotifierLinux::Thread::CleanUp() { | 292 void NetworkChangeNotifierLinux::Thread::CleanUp() { |
316 if (netlink_fd_ != kInvalidSocket) { | |
317 if (HANDLE_EINTR(close(netlink_fd_)) != 0) | |
318 PLOG(ERROR) << "Failed to close socket"; | |
319 netlink_fd_ = kInvalidSocket; | |
320 netlink_watcher_.StopWatchingFileDescriptor(); | |
321 } | |
322 network_manager_api_.CleanUp(); | 293 network_manager_api_.CleanUp(); |
323 | |
324 dns_watcher_.CleanUp(); | 294 dns_watcher_.CleanUp(); |
325 } | 295 } |
326 | 296 |
327 void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) { | |
328 DCHECK_EQ(fd, netlink_fd_); | |
329 ListenForNotifications(); | |
330 } | |
331 | |
332 void NetworkChangeNotifierLinux::Thread::OnFileCanWriteWithoutBlocking( | |
333 int /* fd */) { | |
334 NOTREACHED(); | |
335 } | |
336 | |
337 void NetworkChangeNotifierLinux::Thread::ListenForNotifications() { | |
338 char buf[4096]; | |
339 int rv = ReadNotificationMessage(buf, arraysize(buf)); | |
340 while (rv > 0) { | |
341 if (HandleNetlinkMessage(buf, rv)) { | |
342 VLOG(1) << "Detected IP address changes."; | |
343 NotifyObserversOfIPAddressChange(); | |
344 } | |
345 rv = ReadNotificationMessage(buf, arraysize(buf)); | |
346 } | |
347 | |
348 if (rv == ERR_IO_PENDING) { | |
349 rv = MessageLoopForIO::current()->WatchFileDescriptor(netlink_fd_, false, | |
350 MessageLoopForIO::WATCH_READ, &netlink_watcher_, this); | |
351 LOG_IF(ERROR, !rv) << "Failed to watch netlink socket: " << netlink_fd_; | |
352 } | |
353 } | |
354 | |
355 int NetworkChangeNotifierLinux::Thread::ReadNotificationMessage( | |
356 char* buf, | |
357 size_t len) { | |
358 DCHECK_NE(len, 0u); | |
359 DCHECK(buf); | |
360 memset(buf, 0, len); | |
361 int rv = recv(netlink_fd_, buf, len, 0); | |
362 if (rv > 0) | |
363 return rv; | |
364 | |
365 DCHECK_NE(rv, 0); | |
366 if (errno != EAGAIN && errno != EWOULDBLOCK) { | |
367 PLOG(DFATAL) << "recv"; | |
368 return ERR_FAILED; | |
369 } | |
370 | |
371 return ERR_IO_PENDING; | |
372 } | |
373 | |
374 NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::Create() { | 297 NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::Create() { |
375 return new NetworkChangeNotifierLinux(NULL); | 298 return new NetworkChangeNotifierLinux(NULL); |
376 } | 299 } |
377 | 300 |
378 NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::CreateForTest( | 301 NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::CreateForTest( |
379 dbus::Bus* bus) { | 302 dbus::Bus* bus) { |
380 return new NetworkChangeNotifierLinux(bus); | 303 return new NetworkChangeNotifierLinux(bus); |
381 } | 304 } |
382 | 305 |
383 NetworkChangeNotifierLinux::NetworkChangeNotifierLinux(dbus::Bus* bus) | 306 NetworkChangeNotifierLinux::NetworkChangeNotifierLinux(dbus::Bus* bus) |
384 : notifier_thread_(new Thread(bus)) { | 307 : notifier_thread_(new Thread(bus)) { |
385 // We create this notifier thread because the notification implementation | 308 // We create this notifier thread because the notification implementation |
386 // needs a MessageLoopForIO, and there's no guarantee that | 309 // needs a MessageLoopForIO, and there's no guarantee that |
387 // MessageLoop::current() meets that criterion. | 310 // MessageLoop::current() meets that criterion. |
388 base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0); | 311 base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0); |
389 notifier_thread_->StartWithOptions(thread_options); | 312 notifier_thread_->StartWithOptions(thread_options); |
390 } | 313 } |
391 | 314 |
392 NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() { | 315 NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() { |
393 // Stopping from here allows us to sanity- check that the notifier | 316 // Stopping from here allows us to sanity- check that the notifier |
394 // thread shut down properly. | 317 // thread shut down properly. |
395 notifier_thread_->Stop(); | 318 notifier_thread_->Stop(); |
396 } | 319 } |
397 | 320 |
398 NetworkChangeNotifier::ConnectionType | 321 NetworkChangeNotifier::ConnectionType |
399 NetworkChangeNotifierLinux::GetCurrentConnectionType() const { | 322 NetworkChangeNotifierLinux::GetCurrentConnectionType() const { |
400 return notifier_thread_->GetCurrentConnectionType(); | 323 return notifier_thread_->GetCurrentConnectionType(); |
401 } | 324 } |
402 | 325 |
| 326 const internal::AddressTrackerLinux* |
| 327 NetworkChangeNotifierLinux::GetAddressTrackerInternal() const { |
| 328 return notifier_thread_->address_tracker(); |
| 329 } |
| 330 |
403 } // namespace net | 331 } // namespace net |
OLD | NEW |