diff options
author | wangxianzhu@chromium.org <wangxianzhu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-15 05:12:27 +0000 |
---|---|---|
committer | wangxianzhu@chromium.org <wangxianzhu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-15 05:12:27 +0000 |
commit | c65c6e3150af3dc37471c76b10d2b05cf7367aa3 (patch) | |
tree | d1e2cd5de49cd95893c6ab6ce55de6a145951e4f /tools/android | |
parent | 69f406901670991e1d8a5e44346ffba25741b5cd (diff) | |
download | chromium_src-c65c6e3150af3dc37471c76b10d2b05cf7367aa3.zip chromium_src-c65c6e3150af3dc37471c76b10d2b05cf7367aa3.tar.gz chromium_src-c65c6e3150af3dc37471c76b10d2b05cf7367aa3.tar.bz2 |
tools/android/forwarder (as well as tools/android/common)
'forwarder' is a tool that can forward a TCP port listening on the device
to a port on the host. It works like 'adb forward' but in the reverse
direction. It's useful to run tests on the device that need to access TCP
services on the host.
Review URL: http://codereview.chromium.org/9359003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@122048 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/android')
-rw-r--r-- | tools/android/OWNERS | 6 | ||||
-rw-r--r-- | tools/android/common/adb_connection.cc | 105 | ||||
-rw-r--r-- | tools/android/common/adb_connection.h | 19 | ||||
-rw-r--r-- | tools/android/common/common.gyp | 27 | ||||
-rw-r--r-- | tools/android/common/daemon.cc | 70 | ||||
-rw-r--r-- | tools/android/common/daemon.h | 31 | ||||
-rw-r--r-- | tools/android/common/net.cc | 25 | ||||
-rw-r--r-- | tools/android/common/net.h | 21 | ||||
-rw-r--r-- | tools/android/forwarder/forwarder.cc | 405 | ||||
-rw-r--r-- | tools/android/forwarder/forwarder.gyp | 47 |
10 files changed, 756 insertions, 0 deletions
diff --git a/tools/android/OWNERS b/tools/android/OWNERS new file mode 100644 index 0000000..26eb81a --- /dev/null +++ b/tools/android/OWNERS @@ -0,0 +1,6 @@ +set noparent +jnd@chromium.org +jrg@chromium.org +tonyg@chromium.org +wangxianzhu@chromium.org +yfriedman@chromium.org diff --git a/tools/android/common/adb_connection.cc b/tools/android/common/adb_connection.cc new file mode 100644 index 0000000..c542d16 --- /dev/null +++ b/tools/android/common/adb_connection.cc @@ -0,0 +1,105 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tools/android/common/adb_connection.h" + +#include <arpa/inet.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "tools/android/common/net.h" + +namespace tools { + +int ConnectAdbHostSocket(const char* forward_to) { + // ADB port forward request format: HHHHtcp:port:address. + // HHHH is the hexidecimal length of the "tcp:port:address" part. + const size_t kBufferMaxLength = 30; + const size_t kLengthOfLength = 4; + const size_t kAddressMaxLength = kBufferMaxLength - kLengthOfLength; + + const char kAddressPrefix[] = { 't', 'c', 'p', ':' }; + size_t address_length = arraysize(kAddressPrefix) + strlen(forward_to); + if (address_length > kBufferMaxLength - kLengthOfLength) { + LOG(ERROR) << "Forward to address is too long: " << forward_to; + return -1; + } + + char request[kBufferMaxLength]; + memcpy(request + kLengthOfLength, kAddressPrefix, arraysize(kAddressPrefix)); + memcpy(request + kLengthOfLength + arraysize(kAddressPrefix), + forward_to, strlen(forward_to)); + + char length_buffer[kLengthOfLength + 1]; + snprintf(length_buffer, arraysize(length_buffer), "%04X", + static_cast<int>(address_length)); + memcpy(request, length_buffer, kLengthOfLength); + + int host_socket = socket(AF_INET, SOCK_STREAM, 0); + if (host_socket < 0) { + LOG(ERROR) << "Failed to create adb socket: " << strerror(errno); + return -1; + } + + DisableNagle(host_socket); + + const int kAdbPort = 5037; + sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(kAdbPort); + if (HANDLE_EINTR(connect(host_socket, reinterpret_cast<sockaddr*>(&addr), + sizeof(addr))) < 0) { + LOG(ERROR) << "Failed to connect adb socket: " << strerror(errno); + HANDLE_EINTR(close(host_socket)); + return -1; + } + + size_t bytes_remaining = address_length + kLengthOfLength; + size_t bytes_sent = 0; + while (bytes_remaining > 0) { + int ret = HANDLE_EINTR(send(host_socket, request + bytes_sent, + bytes_remaining, 0)); + if (ret < 0) { + LOG(ERROR) << "Failed to send request: " << strerror(errno); + HANDLE_EINTR(close(host_socket)); + return -1; + } + + bytes_sent += ret; + bytes_remaining -= ret; + } + + const size_t kAdbStatusLength = 4; + char response[kBufferMaxLength]; + int response_length = HANDLE_EINTR(recv(host_socket, response, + kBufferMaxLength, 0)); + if (response_length < kAdbStatusLength || + strncmp("OKAY", response, kAdbStatusLength) != 0) { + char fail_msg_buffer[kBufferMaxLength * 3 + 1]; + char* p = fail_msg_buffer; + for (int i = 0; i < response_length; ++i) { + snprintf(p, 3, "%02x,", static_cast<unsigned char>(response[i])); + p += 3; + } + + if (p > fail_msg_buffer) + *(--p) = 0; // Eliminate the last comma. + LOG(ERROR) << "Bad response from ADB: length: " << response_length + << " data: " << fail_msg_buffer; + HANDLE_EINTR(close(host_socket)); + return -1; + } + + return host_socket; +} + +} // namespace tools + diff --git a/tools/android/common/adb_connection.h b/tools/android/common/adb_connection.h new file mode 100644 index 0000000..48a1a6f --- /dev/null +++ b/tools/android/common/adb_connection.h @@ -0,0 +1,19 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_ANDROID_COMMON_ADB_CONNECTION_H__ +#define TOOLS_ANDROID_COMMON_ADB_CONNECTION_H__ +#pragma once + +namespace tools { + +// Creates a socket that can forward to a host socket through ADB. +// The format of forward_to is <port>:<ip_address>. +// Returns the socket handle, or -1 on any error. +int ConnectAdbHostSocket(const char* forward_to); + +} // namespace tools + +#endif // TOOLS_ANDROID_COMMON_ADB_CONNECTION_H__ + diff --git a/tools/android/common/common.gyp b/tools/android/common/common.gyp new file mode 100644 index 0000000..54e2976 --- /dev/null +++ b/tools/android/common/common.gyp @@ -0,0 +1,27 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'android_tools_common', + 'type': '<(library)', + 'include_dirs': [ + '..', + '../../..', + ], + 'sources': [ + 'adb_connection.cc', + 'daemon.cc', + 'net.cc', + ], + }, + ], +} + +# Local Variables: +# tab-width:2 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/tools/android/common/daemon.cc b/tools/android/common/daemon.cc new file mode 100644 index 0000000..c332b1f --- /dev/null +++ b/tools/android/common/daemon.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tools/android/common/daemon.h" + +#include <signal.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" + +namespace { + +const char kNoSpawnDaemon[] = "D"; + +int g_exit_status = 0; + +void Exit(int unused) { + _exit(g_exit_status); +} + +} // namespace + +namespace tools { + +bool HasHelpSwitch(const CommandLine& command_line) { + return command_line.HasSwitch("h") || command_line.HasSwitch("help"); +} + +bool HasNoSpawnDaemonSwitch(const CommandLine& command_line) { + return command_line.HasSwitch(kNoSpawnDaemon); +} + +void ShowHelp(const char* program, + const char* extra_title, + const char* extra_descriptions) { + printf("Usage: %s [-%s] %s\n" + " -%s stops from spawning a daemon process\n%s", + program, kNoSpawnDaemon, extra_title, kNoSpawnDaemon, + extra_descriptions); +} + +void SpawnDaemon(int exit_status) { + g_exit_status = exit_status; + signal(SIGUSR1, Exit); + + if (fork()) { + // In parent process. + sleep(10); // Wait for the child process to finish setsid(). + NOTREACHED(); + } + + // In child process. + setsid(); // Detach the child process from its parent. + kill(getppid(), SIGUSR1); // Inform the parent process to exit. + + // Close the standard input and outputs, otherwise the process may block + // adbd when the shell exits. + // Comment out these lines if you want to see outputs for debugging. + HANDLE_EINTR(close(0)); + HANDLE_EINTR(close(1)); + HANDLE_EINTR(close(2)); +} + +} // namespace tools + diff --git a/tools/android/common/daemon.h b/tools/android/common/daemon.h new file mode 100644 index 0000000..2bd25d4 --- /dev/null +++ b/tools/android/common/daemon.h @@ -0,0 +1,31 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_ANDROID_COMMON_DAEMON_H__ +#define TOOLS_ANDROID_COMMON_DAEMON_H__ +#pragma once + +#include <string> +#include <vector> + +class CommandLine; + +namespace tools { + +bool HasHelpSwitch(const CommandLine& command_line); + +bool HasNoSpawnDaemonSwitch(const CommandLine& command_line); + +void ShowHelp(const char* program, + const char* extra_title, + const char* extra_descriptions); + +// Spawns a daemon process and exit the current process. +// Any code after this function will be executed in the spawned daemon process. +void SpawnDaemon(int exit_status); + +} // namespace tools + +#endif // TOOLS_ANDROID_COMMON_DAEMON_H__ + diff --git a/tools/android/common/net.cc b/tools/android/common/net.cc new file mode 100644 index 0000000..e50a18a --- /dev/null +++ b/tools/android/common/net.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tools/android/common/net.h" + +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <sys/socket.h> +#include <sys/types.h> + +namespace tools { + +int DisableNagle(int socket) { + int on = 1; + return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); +} + +int DeferAccept(int socket) { + int on = 1; + return setsockopt(socket, IPPROTO_TCP, TCP_DEFER_ACCEPT, &on, sizeof(on)); +} + +} // namespace tools + diff --git a/tools/android/common/net.h b/tools/android/common/net.h new file mode 100644 index 0000000..d17b6f4 --- /dev/null +++ b/tools/android/common/net.h @@ -0,0 +1,21 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef TOOLS_ANDROID_COMMON_NET_H_ +#define TOOLS_ANDROID_COMMON_NET_H_ +#pragma once + +namespace tools { + +// DisableNagle can improve TCP transmission performance. Both Chrome net stack +// and adb tool use it. +int DisableNagle(int socket); + +// Wake up listener only when data arrive. +int DeferAccept(int socket); + +} // namespace tools + +#endif // TOOLS_ANDROID_COMMON_NET_H_ + diff --git a/tools/android/forwarder/forwarder.cc b/tools/android/forwarder/forwarder.cc new file mode 100644 index 0000000..e19fe65 --- /dev/null +++ b/tools/android/forwarder/forwarder.cc @@ -0,0 +1,405 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <errno.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <pthread.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "tools/android/common/adb_connection.h" +#include "tools/android/common/daemon.h" +#include "tools/android/common/net.h" + +namespace { + +const pthread_t kInvalidThread = static_cast<pthread_t>(-1); +volatile bool g_killed = false; + +class Buffer { + public: + Buffer() + : bytes_read_(0), + write_offset_(0) { + } + + bool CanRead() { + return bytes_read_ == 0; + } + + bool CanWrite() { + return write_offset_ < bytes_read_; + } + + int Read(int fd) { + int ret = -1; + if (CanRead()) { + ret = HANDLE_EINTR(read(fd, buffer_, kBufferSize)); + if (ret > 0) + bytes_read_ = ret; + } + return ret; + } + + int Write(int fd) { + int ret = -1; + if (CanWrite()) { + ret = HANDLE_EINTR(write(fd, buffer_ + write_offset_, + bytes_read_ - write_offset_)); + if (ret > 0) { + write_offset_ += ret; + if (write_offset_ == bytes_read_) { + write_offset_ = 0; + bytes_read_ = 0; + } + } + } + return ret; + } + + private: + // A big buffer to let our file-over-http bridge work more like real file. + static const int kBufferSize = 1024 * 128; + int bytes_read_; + int write_offset_; + char buffer_[kBufferSize]; + + DISALLOW_COPY_AND_ASSIGN(Buffer); +}; + +class Server; + +struct ForwarderThreadInfo { + ForwarderThreadInfo(Server* a_server, int a_forwarder_index) + : server(a_server), + forwarder_index(a_forwarder_index) { + } + Server* server; + int forwarder_index; +}; + +struct ForwarderInfo { + time_t start_time; + int socket1; + time_t socket1_last_byte_time; + size_t socket1_bytes; + int socket2; + time_t socket2_last_byte_time; + size_t socket2_bytes; +}; + +class Server { + public: + Server() + : thread_(kInvalidThread), + socket_(-1) { + memset(forward_to_, 0, sizeof(forward_to_)); + memset(&forwarders_, 0, sizeof(forwarders_)); + } + + int GetFreeForwarderIndex() { + for (int i = 0; i < kMaxForwarders; i++) { + if (forwarders_[i].start_time == 0) + return i; + } + return -1; + } + + void DisposeForwarderInfo(int index) { + forwarders_[index].start_time = 0; + } + + ForwarderInfo* GetForwarderInfo(int index) { + return &forwarders_[index]; + } + + void DumpInformation() { + LOG(INFO) << "Server information: " << forward_to_; + LOG(INFO) << "No.: age up(bytes,idle) down(bytes,idle)"; + int count = 0; + time_t now = time(NULL); + for (int i = 0; i < kMaxForwarders; i++) { + const ForwarderInfo& info = forwarders_[i]; + if (info.start_time) { + count++; + LOG(INFO) << count << ": " << now - info.start_time << " up(" + << info.socket1_bytes << "," + << now - info.socket1_last_byte_time << " down(" + << info.socket2_bytes << "," + << now - info.socket2_last_byte_time << ")"; + } + } + } + + void Shutdown() { + if (socket_ >= 0) + shutdown(socket_, SHUT_RDWR); + } + + bool InitSocket(const char* arg); + + void StartThread() { + pthread_create(&thread_, NULL, ServerThread, this); + } + + void JoinThread() { + if (thread_ != kInvalidThread) + pthread_join(thread_, NULL); + } + + private: + static void* ServerThread(void* arg); + + // There are 3 kinds of threads that will access the array: + // 1. Server thread will get a free ForwarderInfo and initialize it; + // 2. Forwarder threads will dispose the ForwarderInfo when it finishes; + // 3. Main thread will iterate and print the forwarders. + // Using an array is not optimal, but can avoid locks or other complex + // inter-thread communication. + static const int kMaxForwarders = 512; + ForwarderInfo forwarders_[kMaxForwarders]; + + pthread_t thread_; + int socket_; + char forward_to_[40]; + + DISALLOW_COPY_AND_ASSIGN(Server); +}; + +// Forwards all outputs from one socket to another socket. +void* ForwarderThread(void* arg) { + ForwarderThreadInfo* thread_info = + reinterpret_cast<ForwarderThreadInfo*>(arg); + Server* server = thread_info->server; + int index = thread_info->forwarder_index; + delete thread_info; + ForwarderInfo* info = server->GetForwarderInfo(index); + int socket1 = info->socket1; + int socket2 = info->socket2; + int nfds = socket1 > socket2 ? socket1 + 1 : socket2 + 1; + fd_set read_fds; + fd_set write_fds; + Buffer buffer1; + Buffer buffer2; + + while (!g_killed) { + FD_ZERO(&read_fds); + if (buffer1.CanRead()) + FD_SET(socket1, &read_fds); + if (buffer2.CanRead()) + FD_SET(socket2, &read_fds); + + FD_ZERO(&write_fds); + if (buffer1.CanWrite()) + FD_SET(socket2, &write_fds); + if (buffer2.CanWrite()) + FD_SET(socket1, &write_fds); + + if (HANDLE_EINTR(select(nfds, &read_fds, &write_fds, NULL, NULL)) <= 0) { + LOG(ERROR) << "Select error: " << strerror(errno); + break; + } + + int now = time(NULL); + if (FD_ISSET(socket1, &read_fds)) { + info->socket1_last_byte_time = now; + int bytes = buffer1.Read(socket1); + if (bytes <= 0) + break; + info->socket1_bytes += bytes; + } + if (FD_ISSET(socket2, &read_fds)) { + info->socket2_last_byte_time = now; + int bytes = buffer2.Read(socket2); + if (bytes <= 0) + break; + info->socket2_bytes += bytes; + } + if (FD_ISSET(socket1, &write_fds)) { + if (buffer2.Write(socket1) <= 0) + break; + } + if (FD_ISSET(socket2, &write_fds)) { + if (buffer1.Write(socket2) <= 0) + break; + } + } + + HANDLE_EINTR(close(socket1)); + HANDLE_EINTR(close(socket2)); + server->DisposeForwarderInfo(index); + return NULL; +} + +// Listens to a server socket. On incoming request, forward it to the host. +// static +void* Server::ServerThread(void* arg) { + Server* server = reinterpret_cast<Server*>(arg); + while (!g_killed) { + int forwarder_index = server->GetFreeForwarderIndex(); + if (forwarder_index < 0) { + LOG(ERROR) << "Too many forwarders"; + continue; + } + + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + int socket = HANDLE_EINTR(accept(server->socket_, + reinterpret_cast<sockaddr*>(&addr), + &addr_len)); + if (socket < 0) { + LOG(ERROR) << "Failed to accept: " << strerror(errno); + break; + } + tools::DisableNagle(socket); + + int host_socket = tools::ConnectAdbHostSocket(server->forward_to_); + if (host_socket >= 0) { + // Set NONBLOCK flag because we use select(). + fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) | O_NONBLOCK); + fcntl(host_socket, F_SETFL, fcntl(host_socket, F_GETFL) | O_NONBLOCK); + + ForwarderInfo* forwarder_info = server->GetForwarderInfo(forwarder_index); + time_t now = time(NULL); + forwarder_info->start_time = now; + forwarder_info->socket1 = socket; + forwarder_info->socket1_last_byte_time = now; + forwarder_info->socket1_bytes = 0; + forwarder_info->socket2 = host_socket; + forwarder_info->socket2_last_byte_time = now; + forwarder_info->socket2_bytes = 0; + + pthread_t thread; + pthread_create(&thread, NULL, ForwarderThread, + new ForwarderThreadInfo(server, forwarder_index)); + } else { + // Close the unused client socket which is failed to connect to host. + HANDLE_EINTR(close(socket)); + } + } + + HANDLE_EINTR(close(server->socket_)); + server->socket_ = -1; + return NULL; +} + +// Format of arg: <Device port>[:<Forward to port>:<Forward to address>] +bool Server::InitSocket(const char* arg) { + char* endptr; + int local_port = static_cast<int>(strtol(arg, &endptr, 10)); + if (local_port <= 0) + return false; + + if (*endptr != ':') { + snprintf(forward_to_, sizeof(forward_to_), "%d:127.0.0.1", local_port); + } else { + strncpy(forward_to_, endptr + 1, sizeof(forward_to_) - 1); + } + + socket_ = socket(AF_INET, SOCK_STREAM, 0); + if (socket_ < 0) { + perror("server socket"); + return false; + } + tools::DisableNagle(socket_); + + sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(local_port); + int reuse_addr = 1; + setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, + &reuse_addr, sizeof(reuse_addr)); + tools::DeferAccept(socket_); + if (HANDLE_EINTR(bind(socket_, reinterpret_cast<sockaddr*>(&addr), + sizeof(addr))) < 0 || + HANDLE_EINTR(listen(socket_, 5)) < 0) { + perror("server bind"); + HANDLE_EINTR(close(socket_)); + socket_ = -1; + return false; + } + + printf("Forwarding device port %d to host %s\n", local_port, forward_to_); + return true; +} + +int g_server_count = 0; +Server* g_servers = NULL; + +void KillHandler(int unused) { + g_killed = true; + for (int i = 0; i < g_server_count; i++) + g_servers[i].Shutdown(); +} + +void DumpInformation(int unused) { + for (int i = 0; i < g_server_count; i++) + g_servers[i].DumpInformation(); +} + +} // namespace + +int main(int argc, char** argv) { + printf("Android device to host TCP forwarder\n"); + printf("Like 'adb forward' but in the reverse direction\n"); + + CommandLine command_line(argc, argv); + CommandLine::StringVector server_args = command_line.GetArgs(); + if (tools::HasHelpSwitch(command_line) || server_args.empty()) { + tools::ShowHelp( + argv[0], + "<Device port>[:<Forward to port>:<Forward to address>] ...", + " <Forward to port> default is <Device port>\n" + " <Forward to address> default is 127.0.0.1\n"); + return 0; + } + + g_servers = new Server[server_args.size()]; + g_server_count = 0; + int failed_count = 0; + for (size_t i = 0; i < server_args.size(); i++) { + if (!g_servers[g_server_count].InitSocket(server_args[i].c_str())) { + printf("Couldn't start forwarder server for port spec: %s\n", + server_args[i].c_str()); + ++failed_count; + } else { + ++g_server_count; + } + } + + if (g_server_count == 0) { + printf("No forwarder servers could be started. Exiting.\n"); + delete [] g_servers; + return failed_count; + } + + if (!tools::HasNoSpawnDaemonSwitch(command_line)) + tools::SpawnDaemon(failed_count); + + signal(SIGTERM, KillHandler); + signal(SIGUSR2, DumpInformation); + + for (int i = 0; i < g_server_count; i++) + g_servers[i].StartThread(); + for (int i = 0; i < g_server_count; i++) + g_servers[i].JoinThread(); + g_server_count = 0; + delete [] g_servers; + + return 0; +} + diff --git a/tools/android/forwarder/forwarder.gyp b/tools/android/forwarder/forwarder.gyp new file mode 100644 index 0000000..75390c3 --- /dev/null +++ b/tools/android/forwarder/forwarder.gyp @@ -0,0 +1,47 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +{ + 'targets': [ + { + 'target_name': 'forwarder', + 'type': 'none', + 'dependencies': [ + 'forwarder_symbols', + ], + 'actions': [ + { + 'action_name': 'strip_forwarder', + 'inputs': ['<(PRODUCT_DIR)/forwarder_symbols'], + 'outputs': ['<(PRODUCT_DIR)/forwarder'], + 'action': [ + '<!(/bin/echo -n $STRIP)', + '--strip-unneeded', + '<@(_inputs)', + '-o', + '<@(_outputs)', + ], + }, + ], + }, { + 'target_name': 'forwarder_symbols', + 'type': 'executable', + 'dependencies': [ + '../../../base/base.gyp:base', + '../common/common.gyp:android_tools_common', + ], + 'include_dirs': [ + '../../..', + ], + 'sources': [ + 'forwarder.cc', + ], + }, + ], +} + +# Local Variables: +# tab-width:2 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=2 shiftwidth=2: |