| 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> | 12 #include <errno.h> |
| 13 #include <resolv.h> |
| 13 #include <sys/socket.h> | 14 #include <sys/socket.h> |
| 14 | 15 |
| 15 #include "base/bind.h" | 16 #include "base/bind.h" |
| 16 #include "base/bind_helpers.h" | 17 #include "base/bind_helpers.h" |
| 17 #include "base/callback.h" | 18 #include "base/callback.h" |
| 18 #include "base/compiler_specific.h" | 19 #include "base/compiler_specific.h" |
| 19 #include "base/eintr_wrapper.h" | 20 #include "base/eintr_wrapper.h" |
| 20 #include "base/file_util.h" | 21 #include "base/file_util.h" |
| 21 #include "base/files/file_path_watcher.h" | 22 #include "base/files/file_path_watcher.h" |
| 22 #include "base/memory/weak_ptr.h" | 23 #include "base/memory/weak_ptr.h" |
| 23 #include "base/synchronization/lock.h" | 24 #include "base/synchronization/lock.h" |
| 24 #include "base/synchronization/waitable_event.h" | 25 #include "base/synchronization/waitable_event.h" |
| 25 #include "base/threading/platform_thread.h" | 26 #include "base/threading/platform_thread.h" |
| 26 #include "base/threading/thread.h" | 27 #include "base/threading/thread.h" |
| 27 #include "dbus/bus.h" | 28 #include "dbus/bus.h" |
| 28 #include "dbus/message.h" | 29 #include "dbus/message.h" |
| 29 #include "dbus/object_proxy.h" | 30 #include "dbus/object_proxy.h" |
| 31 #include "net/base/file_path_watcher_callback.h" |
| 30 #include "net/base/net_errors.h" | 32 #include "net/base/net_errors.h" |
| 31 #include "net/base/network_change_notifier_netlink_linux.h" | 33 #include "net/base/network_change_notifier_netlink_linux.h" |
| 32 | 34 |
| 33 using ::base::files::FilePathWatcher; | 35 using ::base::files::FilePathWatcher; |
| 34 | 36 |
| 37 #ifndef _PATH_RESCONF // Normally defined in <resolv.h> |
| 38 #define _PATH_RESCONF "/etc/resolv.conf" |
| 39 #endif |
| 40 |
| 35 namespace net { | 41 namespace net { |
| 36 | 42 |
| 37 namespace { | 43 namespace { |
| 38 | 44 |
| 39 const int kInvalidSocket = -1; | 45 const int kInvalidSocket = -1; |
| 40 | 46 |
| 41 const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager"; | 47 const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager"; |
| 42 const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager"; | 48 const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager"; |
| 43 const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager"; | 49 const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager"; |
| 44 | 50 |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 230 return false; | 236 return false; |
| 231 } | 237 } |
| 232 } | 238 } |
| 233 | 239 |
| 234 bool NetworkManagerApi::IsCurrentlyOffline() { | 240 bool NetworkManagerApi::IsCurrentlyOffline() { |
| 235 offline_state_initialized_.Wait(); | 241 offline_state_initialized_.Wait(); |
| 236 base::AutoLock lock(is_offline_lock_); | 242 base::AutoLock lock(is_offline_lock_); |
| 237 return is_offline_; | 243 return is_offline_; |
| 238 } | 244 } |
| 239 | 245 |
| 240 class DNSWatchDelegate : public FilePathWatcher::Delegate { | |
| 241 public: | |
| 242 explicit DNSWatchDelegate(const base::Closure& callback) | |
| 243 : callback_(callback) {} | |
| 244 virtual ~DNSWatchDelegate() {} | |
| 245 // FilePathWatcher::Delegate interface | |
| 246 virtual void OnFilePathChanged(const FilePath& path) OVERRIDE; | |
| 247 virtual void OnFilePathError(const FilePath& path) OVERRIDE; | |
| 248 private: | |
| 249 base::Closure callback_; | |
| 250 DISALLOW_COPY_AND_ASSIGN(DNSWatchDelegate); | |
| 251 }; | |
| 252 | |
| 253 void DNSWatchDelegate::OnFilePathChanged(const FilePath& path) { | |
| 254 // Calls NetworkChangeNotifier::NotifyObserversOfDNSChange(). | |
| 255 callback_.Run(); | |
| 256 } | |
| 257 | |
| 258 void DNSWatchDelegate::OnFilePathError(const FilePath& path) { | |
| 259 LOG(ERROR) << "DNSWatchDelegate::OnFilePathError for " << path.value(); | |
| 260 } | |
| 261 | |
| 262 } // namespace | 246 } // namespace |
| 263 | 247 |
| 264 class NetworkChangeNotifierLinux::Thread | 248 class NetworkChangeNotifierLinux::Thread |
| 265 : public base::Thread, public MessageLoopForIO::Watcher { | 249 : public base::Thread, public MessageLoopForIO::Watcher { |
| 266 public: | 250 public: |
| 267 explicit Thread(dbus::Bus* bus); | 251 explicit Thread(dbus::Bus* bus); |
| 268 virtual ~Thread(); | 252 virtual ~Thread(); |
| 269 | 253 |
| 270 // MessageLoopForIO::Watcher: | 254 // MessageLoopForIO::Watcher: |
| 271 virtual void OnFileCanReadWithoutBlocking(int fd); | 255 virtual void OnFileCanReadWithoutBlocking(int fd); |
| 272 virtual void OnFileCanWriteWithoutBlocking(int /* fd */); | 256 virtual void OnFileCanWriteWithoutBlocking(int /* fd */); |
| 273 | 257 |
| 274 // Plumbing for NetworkChangeNotifier::IsCurrentlyOffline. | 258 // Plumbing for NetworkChangeNotifier::IsCurrentlyOffline. |
| 275 // Safe to call from any thread. | 259 // Safe to call from any thread. |
| 276 bool IsCurrentlyOffline() { | 260 bool IsCurrentlyOffline() { |
| 277 return network_manager_api_.IsCurrentlyOffline(); | 261 return network_manager_api_.IsCurrentlyOffline(); |
| 278 } | 262 } |
| 279 | 263 |
| 264 bool IsCurrentlyWatchingDNS() { |
| 265 base::AutoLock lock(watching_dns_lock_); |
| 266 return watching_dns_; |
| 267 } |
| 268 |
| 280 protected: | 269 protected: |
| 281 // base::Thread | 270 // base::Thread |
| 282 virtual void Init(); | 271 virtual void Init(); |
| 283 virtual void CleanUp(); | 272 virtual void CleanUp(); |
| 284 | 273 |
| 285 private: | 274 private: |
| 286 // Starts listening for netlink messages. Also handles the messages if there | 275 // Starts listening for netlink messages. Also handles the messages if there |
| 287 // are any available on the netlink socket. | 276 // are any available on the netlink socket. |
| 288 void ListenForNotifications(); | 277 void ListenForNotifications(); |
| 289 | 278 |
| 290 // Attempts to read from the netlink socket into |buf| of length |len|. | 279 // Attempts to read from the netlink socket into |buf| of length |len|. |
| 291 // Returns the bytes read on synchronous success and ERR_IO_PENDING if the | 280 // Returns the bytes read on synchronous success and ERR_IO_PENDING if the |
| 292 // recv() would block. Otherwise, it returns a net error code. | 281 // recv() would block. Otherwise, it returns a net error code. |
| 293 int ReadNotificationMessage(char* buf, size_t len); | 282 int ReadNotificationMessage(char* buf, size_t len); |
| 294 | 283 |
| 284 // Called from FilePathWatcherCallback. |
| 285 void OnDNSFileChanged(unsigned detail, bool watch_success); |
| 286 |
| 295 // The netlink socket descriptor. | 287 // The netlink socket descriptor. |
| 296 int netlink_fd_; | 288 int netlink_fd_; |
| 297 MessageLoopForIO::FileDescriptorWatcher netlink_watcher_; | 289 MessageLoopForIO::FileDescriptorWatcher netlink_watcher_; |
| 298 | 290 |
| 299 // Technically only needed for ChromeOS, but it's ugly to #ifdef out. | 291 // Used to watch for changes to /etc/resolv.conf and /etc/hosts. |
| 300 base::WeakPtrFactory<Thread> ptr_factory_; | 292 FilePathWatcherCallback resolv_watcher_; |
| 293 FilePathWatcherCallback hosts_watcher_; |
| 301 | 294 |
| 302 // Used to watch for changes to /etc/resolv.conf and /etc/hosts. | 295 base::Lock watching_dns_lock_; |
| 303 scoped_ptr<base::files::FilePathWatcher> resolv_file_watcher_; | 296 bool watching_dns_; |
| 304 scoped_ptr<base::files::FilePathWatcher> hosts_file_watcher_; | |
| 305 scoped_refptr<DNSWatchDelegate> resolv_watcher_delegate_; | |
| 306 scoped_refptr<DNSWatchDelegate> hosts_watcher_delegate_; | |
| 307 | 297 |
| 308 // Used to detect online/offline state changes. | 298 // Used to detect online/offline state changes. |
| 309 NetworkManagerApi network_manager_api_; | 299 NetworkManagerApi network_manager_api_; |
| 310 | 300 |
| 311 DISALLOW_COPY_AND_ASSIGN(Thread); | 301 DISALLOW_COPY_AND_ASSIGN(Thread); |
| 312 }; | 302 }; |
| 313 | 303 |
| 314 NetworkChangeNotifierLinux::Thread::Thread(dbus::Bus* bus) | 304 NetworkChangeNotifierLinux::Thread::Thread(dbus::Bus* bus) |
| 315 : base::Thread("NetworkChangeNotifier"), | 305 : base::Thread("NetworkChangeNotifier"), |
| 316 netlink_fd_(kInvalidSocket), | 306 netlink_fd_(kInvalidSocket), |
| 317 ALLOW_THIS_IN_INITIALIZER_LIST(ptr_factory_(this)), | 307 watching_dns_(false), |
| 318 network_manager_api_( | 308 network_manager_api_( |
| 319 base::Bind(&NetworkChangeNotifier | 309 base::Bind(&NetworkChangeNotifier |
| 320 ::NotifyObserversOfOnlineStateChange), | 310 ::NotifyObserversOfOnlineStateChange), |
| 321 bus) { | 311 bus) { |
| 322 } | 312 } |
| 323 | 313 |
| 324 NetworkChangeNotifierLinux::Thread::~Thread() { | 314 NetworkChangeNotifierLinux::Thread::~Thread() { |
| 325 DCHECK(!Thread::IsRunning()); | 315 DCHECK(!Thread::IsRunning()); |
| 326 } | 316 } |
| 327 | 317 |
| 328 void NetworkChangeNotifierLinux::Thread::Init() { | 318 void NetworkChangeNotifierLinux::Thread::Init() { |
| 329 resolv_file_watcher_.reset(new FilePathWatcher); | 319 base::AutoLock lock(watching_dns_lock_); |
| 330 hosts_file_watcher_.reset(new FilePathWatcher); | 320 watching_dns_ = true; |
| 331 resolv_watcher_delegate_ = new DNSWatchDelegate(base::Bind( | 321 if (!resolv_watcher_.Watch( |
| 332 &NetworkChangeNotifier::NotifyObserversOfDNSChange, | 322 FilePath(FILE_PATH_LITERAL(_PATH_RESCONF)), |
| 333 static_cast<unsigned>(CHANGE_DNS_SETTINGS))); | 323 base::Bind(&Thread::OnDNSFileChanged, |
| 334 hosts_watcher_delegate_ = new DNSWatchDelegate(base::Bind( | 324 base::Unretained(this), |
| 335 &NetworkChangeNotifier::NotifyObserversOfDNSChange, | 325 static_cast<unsigned>(CHANGE_DNS_SETTINGS)))) { |
| 336 static_cast<unsigned>(CHANGE_DNS_HOSTS))); | |
| 337 if (!resolv_file_watcher_->Watch( | |
| 338 FilePath(FILE_PATH_LITERAL("/etc/resolv.conf")), | |
| 339 resolv_watcher_delegate_.get())) { | |
| 340 LOG(ERROR) << "Failed to setup watch for /etc/resolv.conf"; | 326 LOG(ERROR) << "Failed to setup watch for /etc/resolv.conf"; |
| 327 watching_dns_ = false; |
| 341 } | 328 } |
| 342 if (!hosts_file_watcher_->Watch(FilePath(FILE_PATH_LITERAL("/etc/hosts")), | 329 if (!hosts_watcher_.Watch( |
| 343 hosts_watcher_delegate_.get())) { | 330 FilePath(FILE_PATH_LITERAL("/etc/hosts")), |
| 331 base::Bind(&Thread::OnDNSFileChanged, |
| 332 base::Unretained(this), |
| 333 static_cast<unsigned>(CHANGE_DNS_HOSTS)))) { |
| 344 LOG(ERROR) << "Failed to setup watch for /etc/hosts"; | 334 LOG(ERROR) << "Failed to setup watch for /etc/hosts"; |
| 335 watching_dns_ = false; |
| 345 } | 336 } |
| 346 netlink_fd_ = InitializeNetlinkSocket(); | 337 netlink_fd_ = InitializeNetlinkSocket(); |
| 347 if (netlink_fd_ < 0) { | 338 if (netlink_fd_ < 0) { |
| 348 netlink_fd_ = kInvalidSocket; | 339 netlink_fd_ = kInvalidSocket; |
| 349 return; | 340 return; |
| 350 } | 341 } |
| 351 ListenForNotifications(); | 342 ListenForNotifications(); |
| 352 | 343 |
| 353 network_manager_api_.Init(); | 344 network_manager_api_.Init(); |
| 354 } | 345 } |
| 355 | 346 |
| 356 void NetworkChangeNotifierLinux::Thread::CleanUp() { | 347 void NetworkChangeNotifierLinux::Thread::CleanUp() { |
| 357 if (netlink_fd_ != kInvalidSocket) { | 348 if (netlink_fd_ != kInvalidSocket) { |
| 358 if (HANDLE_EINTR(close(netlink_fd_)) != 0) | 349 if (HANDLE_EINTR(close(netlink_fd_)) != 0) |
| 359 PLOG(ERROR) << "Failed to close socket"; | 350 PLOG(ERROR) << "Failed to close socket"; |
| 360 netlink_fd_ = kInvalidSocket; | 351 netlink_fd_ = kInvalidSocket; |
| 361 netlink_watcher_.StopWatchingFileDescriptor(); | 352 netlink_watcher_.StopWatchingFileDescriptor(); |
| 362 } | 353 } |
| 363 // Kill watchers early to make sure they won't try to call | 354 // Kill watchers early to make sure they won't try to call |
| 364 // into us via the delegate during destruction. | 355 // into us via the delegate during destruction. |
| 365 resolv_file_watcher_.reset(); | 356 resolv_watcher_.Cancel(); |
| 366 hosts_file_watcher_.reset(); | 357 hosts_watcher_.Cancel(); |
| 367 | 358 |
| 368 network_manager_api_.CleanUp(); | 359 network_manager_api_.CleanUp(); |
| 369 } | 360 } |
| 370 | 361 |
| 371 void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) { | 362 void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) { |
| 372 DCHECK_EQ(fd, netlink_fd_); | 363 DCHECK_EQ(fd, netlink_fd_); |
| 373 ListenForNotifications(); | 364 ListenForNotifications(); |
| 374 } | 365 } |
| 375 | 366 |
| 376 void NetworkChangeNotifierLinux::Thread::OnFileCanWriteWithoutBlocking( | 367 void NetworkChangeNotifierLinux::Thread::OnFileCanWriteWithoutBlocking( |
| 377 int /* fd */) { | 368 int /* fd */) { |
| 378 NOTREACHED(); | 369 NOTREACHED(); |
| 379 } | 370 } |
| 380 | 371 |
| 381 void NetworkChangeNotifierLinux::Thread::ListenForNotifications() { | 372 void NetworkChangeNotifierLinux::Thread::ListenForNotifications() { |
| 382 char buf[4096]; | 373 char buf[4096]; |
| 383 int rv = ReadNotificationMessage(buf, arraysize(buf)); | 374 int rv = ReadNotificationMessage(buf, arraysize(buf)); |
| 384 while (rv > 0) { | 375 while (rv > 0) { |
| 385 if (HandleNetlinkMessage(buf, rv)) { | 376 if (HandleNetlinkMessage(buf, rv)) { |
| 386 VLOG(1) << "Detected IP address changes."; | 377 VLOG(1) << "Detected IP address changes."; |
| 387 #if defined(OS_CHROMEOS) | |
| 388 // TODO(oshima): chromium-os:8285 - introduced artificial delay to | |
| 389 // work around the issue of network load issue after connection | |
| 390 // restored. See the bug for more details. | |
| 391 // This should be removed once this bug is properly fixed. | |
| 392 const int kObserverNotificationDelayMS = 200; | |
| 393 message_loop()->PostDelayedTask( | |
| 394 FROM_HERE, | |
| 395 base::Bind(&NetworkChangeNotifier::NotifyObserversOfIPAddressChange), | |
| 396 kObserverNotificationDelayMS); | |
| 397 #else | |
| 398 NotifyObserversOfIPAddressChange(); | 378 NotifyObserversOfIPAddressChange(); |
| 399 #endif | |
| 400 } | 379 } |
| 401 rv = ReadNotificationMessage(buf, arraysize(buf)); | 380 rv = ReadNotificationMessage(buf, arraysize(buf)); |
| 402 } | 381 } |
| 403 | 382 |
| 404 if (rv == ERR_IO_PENDING) { | 383 if (rv == ERR_IO_PENDING) { |
| 405 rv = MessageLoopForIO::current()->WatchFileDescriptor(netlink_fd_, false, | 384 rv = MessageLoopForIO::current()->WatchFileDescriptor(netlink_fd_, false, |
| 406 MessageLoopForIO::WATCH_READ, &netlink_watcher_, this); | 385 MessageLoopForIO::WATCH_READ, &netlink_watcher_, this); |
| 407 LOG_IF(ERROR, !rv) << "Failed to watch netlink socket: " << netlink_fd_; | 386 LOG_IF(ERROR, !rv) << "Failed to watch netlink socket: " << netlink_fd_; |
| 408 } | 387 } |
| 409 } | 388 } |
| (...skipping 10 matching lines...) Expand all Loading... |
| 420 | 399 |
| 421 DCHECK_NE(rv, 0); | 400 DCHECK_NE(rv, 0); |
| 422 if (errno != EAGAIN && errno != EWOULDBLOCK) { | 401 if (errno != EAGAIN && errno != EWOULDBLOCK) { |
| 423 PLOG(DFATAL) << "recv"; | 402 PLOG(DFATAL) << "recv"; |
| 424 return ERR_FAILED; | 403 return ERR_FAILED; |
| 425 } | 404 } |
| 426 | 405 |
| 427 return ERR_IO_PENDING; | 406 return ERR_IO_PENDING; |
| 428 } | 407 } |
| 429 | 408 |
| 409 void NetworkChangeNotifierLinux::Thread::OnDNSFileChanged(unsigned detail, |
| 410 bool watch_success) { |
| 411 if (!watch_success) { |
| 412 LOG(ERROR) << "DNS watch failed."; |
| 413 base::AutoLock lock(watching_dns_lock_); |
| 414 watching_dns_ = false; |
| 415 } |
| 416 // Always notify observers so that they can check IsWatchingDNS(). |
| 417 NetworkChangeNotifier::NotifyObserversOfDNSChange(detail); |
| 418 } |
| 419 |
| 430 NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::Create() { | 420 NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::Create() { |
| 431 return new NetworkChangeNotifierLinux(NULL); | 421 return new NetworkChangeNotifierLinux(NULL); |
| 432 } | 422 } |
| 433 | 423 |
| 434 NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::CreateForTest( | 424 NetworkChangeNotifierLinux* NetworkChangeNotifierLinux::CreateForTest( |
| 435 dbus::Bus* bus) { | 425 dbus::Bus* bus) { |
| 436 return new NetworkChangeNotifierLinux(bus); | 426 return new NetworkChangeNotifierLinux(bus); |
| 437 } | 427 } |
| 438 | 428 |
| 439 NetworkChangeNotifierLinux::NetworkChangeNotifierLinux(dbus::Bus* bus) | 429 NetworkChangeNotifierLinux::NetworkChangeNotifierLinux(dbus::Bus* bus) |
| 440 : notifier_thread_(new Thread(bus)) { | 430 : notifier_thread_(new Thread(bus)) { |
| 441 // We create this notifier thread because the notification implementation | 431 // We create this notifier thread because the notification implementation |
| 442 // needs a MessageLoopForIO, and there's no guarantee that | 432 // needs a MessageLoopForIO, and there's no guarantee that |
| 443 // MessageLoop::current() meets that criterion. | 433 // MessageLoop::current() meets that criterion. |
| 444 base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0); | 434 base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0); |
| 445 notifier_thread_->StartWithOptions(thread_options); | 435 notifier_thread_->StartWithOptions(thread_options); |
| 446 } | 436 } |
| 447 | 437 |
| 448 NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() { | 438 NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() { |
| 449 // Stopping from here allows us to sanity- check that the notifier | 439 // Stopping from here allows us to sanity- check that the notifier |
| 450 // thread shut down properly. | 440 // thread shut down properly. |
| 451 notifier_thread_->Stop(); | 441 notifier_thread_->Stop(); |
| 452 } | 442 } |
| 453 | 443 |
| 454 bool NetworkChangeNotifierLinux::IsCurrentlyOffline() const { | 444 bool NetworkChangeNotifierLinux::IsCurrentlyOffline() const { |
| 455 return notifier_thread_->IsCurrentlyOffline(); | 445 return notifier_thread_->IsCurrentlyOffline(); |
| 456 } | 446 } |
| 457 | 447 |
| 448 bool NetworkChangeNotifierLinux::IsCurrentlyWatchingDNS() const { |
| 449 return notifier_thread_->IsCurrentlyWatchingDNS(); |
| 450 } |
| 451 |
| 458 } // namespace net | 452 } // namespace net |
| OLD | NEW |