• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "net/base/network_change_notifier_linux.h"
6 
7 #include <errno.h>
8 #include <sys/socket.h>
9 
10 #include "base/compiler_specific.h"
11 #include "base/eintr_wrapper.h"
12 #include "base/task.h"
13 #include "base/threading/thread.h"
14 #include "net/base/net_errors.h"
15 #include "net/base/network_change_notifier_netlink_linux.h"
16 
17 namespace net {
18 
19 namespace {
20 
21 const int kInvalidSocket = -1;
22 
23 }  // namespace
24 
25 class NetworkChangeNotifierLinux::Thread
26     : public base::Thread, public MessageLoopForIO::Watcher {
27  public:
28   Thread();
29   virtual ~Thread();
30 
31   // MessageLoopForIO::Watcher:
32   virtual void OnFileCanReadWithoutBlocking(int fd);
33   virtual void OnFileCanWriteWithoutBlocking(int /* fd */);
34 
35  protected:
36   // base::Thread
37   virtual void Init();
38   virtual void CleanUp();
39 
40  private:
NotifyObserversOfIPAddressChange()41   void NotifyObserversOfIPAddressChange() {
42     NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
43   }
44 
45   // Starts listening for netlink messages.  Also handles the messages if there
46   // are any available on the netlink socket.
47   void ListenForNotifications();
48 
49   // Attempts to read from the netlink socket into |buf| of length |len|.
50   // Returns the bytes read on synchronous success and ERR_IO_PENDING if the
51   // recv() would block.  Otherwise, it returns a net error code.
52   int ReadNotificationMessage(char* buf, size_t len);
53 
54   // The netlink socket descriptor.
55   int netlink_fd_;
56   MessageLoopForIO::FileDescriptorWatcher netlink_watcher_;
57 
58   // Technically only needed for ChromeOS, but it's ugly to #ifdef out.
59   ScopedRunnableMethodFactory<Thread> method_factory_;
60 
61   DISALLOW_COPY_AND_ASSIGN(Thread);
62 };
63 
Thread()64 NetworkChangeNotifierLinux::Thread::Thread()
65     : base::Thread("NetworkChangeNotifier"),
66       netlink_fd_(kInvalidSocket),
67       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {}
68 
~Thread()69 NetworkChangeNotifierLinux::Thread::~Thread() {}
70 
Init()71 void NetworkChangeNotifierLinux::Thread::Init() {
72   netlink_fd_ = InitializeNetlinkSocket();
73   if (netlink_fd_ < 0) {
74     netlink_fd_ = kInvalidSocket;
75     return;
76   }
77   ListenForNotifications();
78 }
79 
CleanUp()80 void NetworkChangeNotifierLinux::Thread::CleanUp() {
81   if (netlink_fd_ != kInvalidSocket) {
82     if (HANDLE_EINTR(close(netlink_fd_)) != 0)
83       PLOG(ERROR) << "Failed to close socket";
84     netlink_fd_ = kInvalidSocket;
85     netlink_watcher_.StopWatchingFileDescriptor();
86   }
87 }
88 
OnFileCanReadWithoutBlocking(int fd)89 void NetworkChangeNotifierLinux::Thread::OnFileCanReadWithoutBlocking(int fd) {
90   DCHECK_EQ(fd, netlink_fd_);
91   ListenForNotifications();
92 }
93 
OnFileCanWriteWithoutBlocking(int)94 void NetworkChangeNotifierLinux::Thread::OnFileCanWriteWithoutBlocking(
95     int /* fd */) {
96   NOTREACHED();
97 }
98 
ListenForNotifications()99 void NetworkChangeNotifierLinux::Thread::ListenForNotifications() {
100   char buf[4096];
101   int rv = ReadNotificationMessage(buf, arraysize(buf));
102   while (rv > 0) {
103     if (HandleNetlinkMessage(buf, rv)) {
104       VLOG(1) << "Detected IP address changes.";
105 #if defined(OS_CHROMEOS)
106       // TODO(oshima): chromium-os:8285 - introduced artificial delay to
107       // work around the issue of network load issue after connection
108       // restored. See the bug for more details.
109       //  This should be removed once this bug is properly fixed.
110       const int kObserverNotificationDelayMS = 200;
111       message_loop()->PostDelayedTask(
112           FROM_HERE,
113           method_factory_.NewRunnableMethod(
114               &Thread::NotifyObserversOfIPAddressChange),
115           kObserverNotificationDelayMS);
116 #else
117       NotifyObserversOfIPAddressChange();
118 #endif
119     }
120     rv = ReadNotificationMessage(buf, arraysize(buf));
121   }
122 
123   if (rv == ERR_IO_PENDING) {
124     rv = MessageLoopForIO::current()->WatchFileDescriptor(netlink_fd_, false,
125         MessageLoopForIO::WATCH_READ, &netlink_watcher_, this);
126     LOG_IF(ERROR, !rv) << "Failed to watch netlink socket: " << netlink_fd_;
127   }
128 }
129 
ReadNotificationMessage(char * buf,size_t len)130 int NetworkChangeNotifierLinux::Thread::ReadNotificationMessage(
131     char* buf,
132     size_t len) {
133   DCHECK_NE(len, 0u);
134   DCHECK(buf);
135   memset(buf, 0, sizeof(buf));
136   int rv = recv(netlink_fd_, buf, len, 0);
137   if (rv > 0)
138     return rv;
139 
140   DCHECK_NE(rv, 0);
141   if (errno != EAGAIN && errno != EWOULDBLOCK) {
142     PLOG(DFATAL) << "recv";
143     return ERR_FAILED;
144   }
145 
146   return ERR_IO_PENDING;
147 }
148 
NetworkChangeNotifierLinux()149 NetworkChangeNotifierLinux::NetworkChangeNotifierLinux()
150     : notifier_thread_(new Thread) {
151   // We create this notifier thread because the notification implementation
152   // needs a MessageLoopForIO, and there's no guarantee that
153   // MessageLoop::current() meets that criterion.
154   base::Thread::Options thread_options(MessageLoop::TYPE_IO, 0);
155   notifier_thread_->StartWithOptions(thread_options);
156 }
157 
~NetworkChangeNotifierLinux()158 NetworkChangeNotifierLinux::~NetworkChangeNotifierLinux() {
159   // We don't need to explicitly Stop(), but doing so allows us to sanity-
160   // check that the notifier thread shut down properly.
161   notifier_thread_->Stop();
162 }
163 
IsCurrentlyOffline() const164 bool NetworkChangeNotifierLinux::IsCurrentlyOffline() const {
165   // TODO(eroman): http://crbug.com/53473
166   return false;
167 }
168 
169 }  // namespace net
170