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/files/file_path_watcher.h" | |
22 #include "base/memory/weak_ptr.h" | 21 #include "base/memory/weak_ptr.h" |
23 #include "base/synchronization/lock.h" | 22 #include "base/synchronization/lock.h" |
24 #include "base/synchronization/waitable_event.h" | 23 #include "base/synchronization/waitable_event.h" |
25 #include "base/threading/platform_thread.h" | 24 #include "base/threading/platform_thread.h" |
26 #include "base/threading/thread.h" | 25 #include "base/threading/thread.h" |
27 #include "base/threading/thread_restrictions.h" | 26 #include "base/threading/thread_restrictions.h" |
28 #include "dbus/bus.h" | 27 #include "dbus/bus.h" |
29 #include "dbus/message.h" | 28 #include "dbus/message.h" |
30 #include "dbus/object_proxy.h" | 29 #include "dbus/object_proxy.h" |
31 #include "net/base/net_errors.h" | 30 #include "net/base/net_errors.h" |
32 #include "net/base/network_change_notifier_netlink_linux.h" | 31 #include "net/base/network_change_notifier_netlink_linux.h" |
33 | 32 #include "net/dns/dns_config_watcher.h" |
34 using ::base::files::FilePathWatcher; | |
35 | 33 |
36 namespace net { | 34 namespace net { |
37 | 35 |
38 namespace { | 36 namespace { |
39 | 37 |
40 const int kInvalidSocket = -1; | 38 const int kInvalidSocket = -1; |
41 | 39 |
42 const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager"; | 40 const char kNetworkManagerServiceName[] = "org.freedesktop.NetworkManager"; |
43 const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager"; | 41 const char kNetworkManagerPath[] = "/org/freedesktop/NetworkManager"; |
44 const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager"; | 42 const char kNetworkManagerInterface[] = "org.freedesktop.NetworkManager"; |
(...skipping 12 matching lines...) Expand all Loading... |
57 NM_STATE_UNKNOWN = 0, | 55 NM_STATE_UNKNOWN = 0, |
58 NM_STATE_ASLEEP = 10, | 56 NM_STATE_ASLEEP = 10, |
59 NM_STATE_DISCONNECTED = 20, | 57 NM_STATE_DISCONNECTED = 20, |
60 NM_STATE_DISCONNECTING = 30, | 58 NM_STATE_DISCONNECTING = 30, |
61 NM_STATE_CONNECTING = 40, | 59 NM_STATE_CONNECTING = 40, |
62 NM_STATE_CONNECTED_LOCAL = 50, | 60 NM_STATE_CONNECTED_LOCAL = 50, |
63 NM_STATE_CONNECTED_SITE = 60, | 61 NM_STATE_CONNECTED_SITE = 60, |
64 NM_STATE_CONNECTED_GLOBAL = 70 | 62 NM_STATE_CONNECTED_GLOBAL = 70 |
65 }; | 63 }; |
66 | 64 |
67 class DNSWatchDelegate : public FilePathWatcher::Delegate { | |
68 public: | |
69 explicit DNSWatchDelegate(const base::Closure& callback) | |
70 : callback_(callback) {} | |
71 virtual ~DNSWatchDelegate() {} | |
72 // FilePathWatcher::Delegate interface | |
73 virtual void OnFilePathChanged(const FilePath& path) OVERRIDE; | |
74 virtual void OnFilePathError(const FilePath& path) OVERRIDE; | |
75 private: | |
76 base::Closure callback_; | |
77 DISALLOW_COPY_AND_ASSIGN(DNSWatchDelegate); | |
78 }; | |
79 | |
80 void DNSWatchDelegate::OnFilePathChanged(const FilePath& path) { | |
81 // Calls NetworkChangeNotifier::NotifyObserversOfDNSChange(). | |
82 callback_.Run(); | |
83 } | |
84 | |
85 void DNSWatchDelegate::OnFilePathError(const FilePath& path) { | |
86 LOG(ERROR) << "DNSWatchDelegate::OnFilePathError for " << path.value(); | |
87 } | |
88 | |
89 } // namespace | 65 } // namespace |
90 | 66 |
91 // A wrapper around NetworkManager's D-Bus API. | 67 // A wrapper around NetworkManager's D-Bus API. |
92 class NetworkManagerApi { | 68 class NetworkManagerApi { |
93 public: | 69 public: |
94 NetworkManagerApi(const base::Closure& notification_callback, dbus::Bus* bus) | 70 NetworkManagerApi(const base::Closure& notification_callback, dbus::Bus* bus) |
95 : is_offline_(false), | 71 : is_offline_(false), |
96 offline_state_initialized_(true /*manual_reset*/, false), | 72 offline_state_initialized_(true /*manual_reset*/, false), |
97 notification_callback_(notification_callback), | 73 notification_callback_(notification_callback), |
98 helper_thread_id_(base::kInvalidThreadId), | 74 helper_thread_id_(base::kInvalidThreadId), |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
264 return is_offline_; | 240 return is_offline_; |
265 } | 241 } |
266 | 242 |
267 class NetworkChangeNotifierLinux::Thread | 243 class NetworkChangeNotifierLinux::Thread |
268 : public base::Thread, public MessageLoopForIO::Watcher { | 244 : public base::Thread, public MessageLoopForIO::Watcher { |
269 public: | 245 public: |
270 explicit Thread(dbus::Bus* bus); | 246 explicit Thread(dbus::Bus* bus); |
271 virtual ~Thread(); | 247 virtual ~Thread(); |
272 | 248 |
273 // MessageLoopForIO::Watcher: | 249 // MessageLoopForIO::Watcher: |
274 virtual void OnFileCanReadWithoutBlocking(int fd); | 250 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; |
275 virtual void OnFileCanWriteWithoutBlocking(int /* fd */); | 251 virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE; |
276 | 252 |
277 // Plumbing for NetworkChangeNotifier::IsCurrentlyOffline. | 253 // Plumbing for NetworkChangeNotifier::IsCurrentlyOffline. |
278 // Safe to call from any thread. | 254 // Safe to call from any thread. |
279 bool IsCurrentlyOffline() { | 255 bool IsCurrentlyOffline() { |
280 return network_manager_api_.IsCurrentlyOffline(); | 256 return network_manager_api_.IsCurrentlyOffline(); |
281 } | 257 } |
282 | 258 |
283 protected: | 259 protected: |
284 // base::Thread | 260 // base::Thread |
285 virtual void Init(); | 261 virtual void Init() OVERRIDE; |
286 virtual void CleanUp(); | 262 virtual void CleanUp() OVERRIDE; |
287 | 263 |
288 private: | 264 private: |
289 // Starts listening for netlink messages. Also handles the messages if there | 265 // Starts listening for netlink messages. Also handles the messages if there |
290 // are any available on the netlink socket. | 266 // are any available on the netlink socket. |
291 void ListenForNotifications(); | 267 void ListenForNotifications(); |
292 | 268 |
293 // Attempts to read from the netlink socket into |buf| of length |len|. | 269 // Attempts to read from the netlink socket into |buf| of length |len|. |
294 // Returns the bytes read on synchronous success and ERR_IO_PENDING if the | 270 // Returns the bytes read on synchronous success and ERR_IO_PENDING if the |
295 // recv() would block. Otherwise, it returns a net error code. | 271 // recv() would block. Otherwise, it returns a net error code. |
296 int ReadNotificationMessage(char* buf, size_t len); | 272 int ReadNotificationMessage(char* buf, size_t len); |
297 | 273 |
298 // The netlink socket descriptor. | 274 // The netlink socket descriptor. |
299 int netlink_fd_; | 275 int netlink_fd_; |
300 MessageLoopForIO::FileDescriptorWatcher netlink_watcher_; | 276 MessageLoopForIO::FileDescriptorWatcher netlink_watcher_; |
301 | 277 |
302 // Technically only needed for ChromeOS, but it's ugly to #ifdef out. | |
303 base::WeakPtrFactory<Thread> ptr_factory_; | |
304 | |
305 // Used to watch for changes to /etc/resolv.conf and /etc/hosts. | |
306 scoped_ptr<base::files::FilePathWatcher> resolv_file_watcher_; | |
307 scoped_ptr<base::files::FilePathWatcher> hosts_file_watcher_; | |
308 scoped_refptr<DNSWatchDelegate> resolv_watcher_delegate_; | |
309 scoped_refptr<DNSWatchDelegate> hosts_watcher_delegate_; | |
310 | |
311 // Used to detect online/offline state changes. | 278 // Used to detect online/offline state changes. |
312 NetworkManagerApi network_manager_api_; | 279 NetworkManagerApi network_manager_api_; |
313 | 280 |
| 281 internal::DnsConfigWatcher dns_watcher_; |
| 282 |
314 DISALLOW_COPY_AND_ASSIGN(Thread); | 283 DISALLOW_COPY_AND_ASSIGN(Thread); |
315 }; | 284 }; |
316 | 285 |
317 NetworkChangeNotifierLinux::Thread::Thread(dbus::Bus* bus) | 286 NetworkChangeNotifierLinux::Thread::Thread(dbus::Bus* bus) |
318 : base::Thread("NetworkChangeNotifier"), | 287 : base::Thread("NetworkChangeNotifier"), |
319 netlink_fd_(kInvalidSocket), | 288 netlink_fd_(kInvalidSocket), |
320 ALLOW_THIS_IN_INITIALIZER_LIST(ptr_factory_(this)), | |
321 network_manager_api_( | 289 network_manager_api_( |
322 base::Bind(&NetworkChangeNotifier | 290 base::Bind(&NetworkChangeNotifier |
323 ::NotifyObserversOfOnlineStateChange), | 291 ::NotifyObserversOfOnlineStateChange), |
324 bus) { | 292 bus) { |
325 } | 293 } |
326 | 294 |
327 NetworkChangeNotifierLinux::Thread::~Thread() { | 295 NetworkChangeNotifierLinux::Thread::~Thread() { |
328 DCHECK(!Thread::IsRunning()); | 296 DCHECK(!Thread::IsRunning()); |
329 } | 297 } |
330 | 298 |
331 void NetworkChangeNotifierLinux::Thread::Init() { | 299 void NetworkChangeNotifierLinux::Thread::Init() { |
332 resolv_file_watcher_.reset(new FilePathWatcher); | |
333 hosts_file_watcher_.reset(new FilePathWatcher); | |
334 resolv_watcher_delegate_ = new DNSWatchDelegate(base::Bind( | |
335 &NetworkChangeNotifier::NotifyObserversOfDNSChange, | |
336 static_cast<unsigned>(CHANGE_DNS_SETTINGS))); | |
337 hosts_watcher_delegate_ = new DNSWatchDelegate(base::Bind( | |
338 &NetworkChangeNotifier::NotifyObserversOfDNSChange, | |
339 static_cast<unsigned>(CHANGE_DNS_HOSTS))); | |
340 if (!resolv_file_watcher_->Watch( | |
341 FilePath(FILE_PATH_LITERAL("/etc/resolv.conf")), | |
342 resolv_watcher_delegate_.get())) { | |
343 LOG(ERROR) << "Failed to setup watch for /etc/resolv.conf"; | |
344 } | |
345 if (!hosts_file_watcher_->Watch(FilePath(FILE_PATH_LITERAL("/etc/hosts")), | |
346 hosts_watcher_delegate_.get())) { | |
347 LOG(ERROR) << "Failed to setup watch for /etc/hosts"; | |
348 } | |
349 netlink_fd_ = InitializeNetlinkSocket(); | 300 netlink_fd_ = InitializeNetlinkSocket(); |
350 if (netlink_fd_ < 0) { | 301 if (netlink_fd_ < 0) { |
351 netlink_fd_ = kInvalidSocket; | 302 netlink_fd_ = kInvalidSocket; |
352 return; | 303 return; |
353 } | 304 } |
354 ListenForNotifications(); | 305 ListenForNotifications(); |
355 | 306 |
356 network_manager_api_.Init(); | 307 network_manager_api_.Init(); |
| 308 |
| 309 dns_watcher_.Init(); |
357 } | 310 } |
358 | 311 |
359 void NetworkChangeNotifierLinux::Thread::CleanUp() { | 312 void NetworkChangeNotifierLinux::Thread::CleanUp() { |
360 if (netlink_fd_ != kInvalidSocket) { | 313 if (netlink_fd_ != kInvalidSocket) { |
361 if (HANDLE_EINTR(close(netlink_fd_)) != 0) | 314 if (HANDLE_EINTR(close(netlink_fd_)) != 0) |
362 PLOG(ERROR) << "Failed to close socket"; | 315 PLOG(ERROR) << "Failed to close socket"; |
363 netlink_fd_ = kInvalidSocket; | 316 netlink_fd_ = kInvalidSocket; |
364 netlink_watcher_.StopWatchingFileDescriptor(); | 317 netlink_watcher_.StopWatchingFileDescriptor(); |
365 } | 318 } |
366 // Kill watchers early to make sure they won't try to call | 319 network_manager_api_.CleanUp(); |
367 // into us via the delegate during destruction. | |
368 resolv_file_watcher_.reset(); | |
369 hosts_file_watcher_.reset(); | |
370 | 320 |
371 network_manager_api_.CleanUp(); | 321 dns_watcher_.CleanUp(); |
372 } | 322 } |
373 | 323 |
374 void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) { | 324 void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) { |
375 DCHECK_EQ(fd, netlink_fd_); | 325 DCHECK_EQ(fd, netlink_fd_); |
376 ListenForNotifications(); | 326 ListenForNotifications(); |
377 } | 327 } |
378 | 328 |
379 void NetworkChangeNotifierLinux::Thread::OnFileCanWriteWithoutBlocking( | 329 void NetworkChangeNotifierLinux::Thread::OnFileCanWriteWithoutBlocking( |
380 int /* fd */) { | 330 int /* fd */) { |
381 NOTREACHED(); | 331 NOTREACHED(); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
440 // Stopping from here allows us to sanity- check that the notifier | 390 // Stopping from here allows us to sanity- check that the notifier |
441 // thread shut down properly. | 391 // thread shut down properly. |
442 notifier_thread_->Stop(); | 392 notifier_thread_->Stop(); |
443 } | 393 } |
444 | 394 |
445 bool NetworkChangeNotifierLinux::IsCurrentlyOffline() const { | 395 bool NetworkChangeNotifierLinux::IsCurrentlyOffline() const { |
446 return notifier_thread_->IsCurrentlyOffline(); | 396 return notifier_thread_->IsCurrentlyOffline(); |
447 } | 397 } |
448 | 398 |
449 } // namespace net | 399 } // namespace net |
OLD | NEW |