| 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 |