OLD | NEW |
| (Empty) |
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 "chrome/browser/sync/notifier/base/async_network_alive.h" | |
6 | |
7 #include <sys/socket.h> | |
8 #include <asm/types.h> | |
9 #include <linux/netlink.h> | |
10 #include <linux/rtnetlink.h> | |
11 | |
12 #include "base/logging.h" | |
13 #include "base/platform_file.h" | |
14 #include "talk/base/physicalsocketserver.h" | |
15 | |
16 using base::kInvalidPlatformFileValue; | |
17 | |
18 namespace notifier { | |
19 | |
20 class AsyncNetworkAliveLinux : public AsyncNetworkAlive { | |
21 public: | |
22 AsyncNetworkAliveLinux() { | |
23 if (pipe(exit_pipe_) == -1) { | |
24 PLOG(ERROR) << "Could not create pipe for exit signal."; | |
25 exit_pipe_[0] = kInvalidPlatformFileValue; | |
26 exit_pipe_[1] = kInvalidPlatformFileValue; | |
27 } | |
28 } | |
29 | |
30 virtual ~AsyncNetworkAliveLinux() { | |
31 if (exit_pipe_[1] != kInvalidPlatformFileValue) { | |
32 // Ensure that we've signalled the thread to quit. | |
33 char data = 0; | |
34 if (write(exit_pipe_[1], &data, 1) == -1) { | |
35 PLOG(WARNING) << "Error sending error signal to AsyncNetworkAliveLinux"; | |
36 } | |
37 close(exit_pipe_[1]); | |
38 exit_pipe_[1] = kInvalidPlatformFileValue; | |
39 } | |
40 if (exit_pipe_[0] != kInvalidPlatformFileValue) { | |
41 close(exit_pipe_[0]); | |
42 exit_pipe_[0] = kInvalidPlatformFileValue; | |
43 } | |
44 } | |
45 | |
46 protected: | |
47 // SignalThread Interface | |
48 virtual void DoWork() { | |
49 if (exit_pipe_[0] == kInvalidPlatformFileValue) { | |
50 PLOG(ERROR) << "No exit flag to listen for."; | |
51 // If we don't have an exit flag to listen for, set the error flag and | |
52 // abort. | |
53 error_ = true; | |
54 return; | |
55 } | |
56 | |
57 // This function listens for changes to network interfaces, and link state. | |
58 // It's copied from syncapi.cc. | |
59 struct sockaddr_nl socket_address; | |
60 | |
61 memset(&socket_address, 0, sizeof(socket_address)); | |
62 socket_address.nl_family = AF_NETLINK; | |
63 socket_address.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; | |
64 | |
65 // NETLINK_ROUTE is the protocol used to update the kernel routing table. | |
66 int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); | |
67 bind(fd, (struct sockaddr *) &socket_address, sizeof(socket_address)); | |
68 | |
69 fd_set rdfs; | |
70 FD_ZERO(&rdfs); | |
71 FD_SET(fd, &rdfs); | |
72 FD_SET(exit_pipe_[0], &rdfs); | |
73 | |
74 int max_fd = fd > exit_pipe_[0] ? fd : exit_pipe_[0]; | |
75 | |
76 int result = select(max_fd + 1, &rdfs, NULL, NULL, NULL); | |
77 | |
78 if (result <= 0) { | |
79 error_ = true; | |
80 PLOG(ERROR) << "select() returned unexpected result " << result; | |
81 close(fd); | |
82 close(exit_pipe_[0]); | |
83 exit_pipe_[0] = kInvalidPlatformFileValue; | |
84 return; | |
85 } | |
86 | |
87 // Since we received a change from the socket, read the change in. | |
88 if (FD_ISSET(fd, &rdfs)) { | |
89 char buf[4096]; | |
90 struct iovec iov = { buf, sizeof(buf) }; | |
91 struct sockaddr_nl sa; | |
92 | |
93 struct msghdr msg = { (void *)&sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; | |
94 recvmsg(fd, &msg, 0); | |
95 } | |
96 | |
97 close(fd); | |
98 | |
99 // If exit_pipe was written to, we must be shutting down. | |
100 if (exit_pipe_[0] == kInvalidPlatformFileValue || | |
101 FD_ISSET(exit_pipe_[0], &rdfs)) { | |
102 alive_ = false; | |
103 error_ = true; | |
104 close(exit_pipe_[0]); | |
105 exit_pipe_[0] = kInvalidPlatformFileValue; | |
106 return; | |
107 } | |
108 | |
109 // If there is an active connection, check that talk.google.com:5222 | |
110 // is reachable. | |
111 talk_base::PhysicalSocketServer physical; | |
112 scoped_ptr<talk_base::Socket> socket(physical.CreateSocket(SOCK_STREAM)); | |
113 if (socket->Connect(talk_base::SocketAddress("talk.google.com", 5222))) { | |
114 alive_ = false; | |
115 } else { | |
116 alive_ = true; | |
117 } | |
118 | |
119 close(exit_pipe_[0]); | |
120 exit_pipe_[0] = kInvalidPlatformFileValue; | |
121 } | |
122 | |
123 virtual void OnWorkStop() { | |
124 if (exit_pipe_[1] != kInvalidPlatformFileValue) { | |
125 char data = 0; | |
126 // We can't ignore the return value on write(), since that generates a | |
127 // compile warning. However, since we're exiting, there's nothing we can | |
128 // do if this fails except to log it. | |
129 if (write(exit_pipe_[1], &data, 1) == -1) { | |
130 PLOG(WARNING) << "Error sending error signal to AsyncNetworkAliveLinux"; | |
131 } | |
132 } | |
133 } | |
134 | |
135 private: | |
136 int exit_pipe_[2]; | |
137 DISALLOW_COPY_AND_ASSIGN(AsyncNetworkAliveLinux); | |
138 }; | |
139 | |
140 AsyncNetworkAlive* AsyncNetworkAlive::Create() { | |
141 return new AsyncNetworkAliveLinux(); | |
142 } | |
143 | |
144 } // namespace notifier | |
OLD | NEW |