1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <fcntl.h>
17 #include <poll.h>
18 #include <unistd.h>
19
20 #include <mutex>
21 #include <thread>
22
23 #include <android-base/logging.h>
24 #include <gflags/gflags.h>
25
26 #include "common/libs/fs/shared_buf.h"
27 #include "common/libs/fs/shared_fd.h"
28 #include "host/libs/config/logging.h"
29
30 DEFINE_int32(fifo_in, -1, "A pipe for incoming communication");
31 DEFINE_int32(fifo_out, -1, "A pipe for outgoing communication");
32 DEFINE_int32(data_port, -1, "TCP port to connect to");
33 DEFINE_string(data_path, "", "Unix server socket path to connect to");
34 DEFINE_int32(buffer_size, -1, "The buffer size");
35 DEFINE_int32(dump_packet_size, -1,
36 "Dump incoming/outgoing packets up to given size");
37
38 namespace cuttlefish {
39 namespace {
40
OpenSocket(int port)41 SharedFD OpenSocket(int port) {
42 static std::mutex mutex;
43 std::unique_lock<std::mutex> lock(mutex);
44 for (;;) {
45 SharedFD fd = SharedFD::SocketLocalClient(port, SOCK_STREAM);
46 if (fd->IsOpen()) {
47 return fd;
48 }
49 LOG(ERROR) << "Failed to open socket: " << fd->StrError();
50 // Wait a little and try again
51 sleep(1);
52 }
53 }
54
OpenSocket(const std::string & path)55 SharedFD OpenSocket(const std::string& path) {
56 static std::mutex mutex;
57 std::unique_lock<std::mutex> lock(mutex);
58 for (;;) {
59 SharedFD fd = SharedFD::SocketLocalClient(path, false, SOCK_STREAM);
60 if (fd->IsOpen()) {
61 return fd;
62 }
63 LOG(ERROR) << "Failed to open socket: " << fd->StrError();
64 // Wait a little and try again
65 sleep(1);
66 }
67 }
68
DumpPackets(const char * prefix,char * buf,int size)69 void DumpPackets(const char* prefix, char* buf, int size) {
70 if (FLAGS_dump_packet_size < 0 || size <= 0) {
71 return;
72 }
73 char bytes_string[1001] = {0};
74 int len = FLAGS_dump_packet_size < size ? FLAGS_dump_packet_size : size;
75 for (int i = 0; i < len; i++) {
76 if ((i + 1) * 5 > sizeof(bytes_string)) {
77 // Buffer out of bounds
78 break;
79 }
80 sprintf(bytes_string + (i * 5), "0x%02x ", buf[i]);
81 }
82 if (len < size) {
83 LOG(DEBUG) << prefix << ": sz=" << size << ", first " << len << " bytes=["
84 << bytes_string << "...]";
85 } else {
86 LOG(DEBUG) << prefix << ": sz=" << size << ", bytes=[" << bytes_string
87 << "]";
88 }
89 }
90
TcpConnectorMain(int argc,char ** argv)91 int TcpConnectorMain(int argc, char** argv) {
92 DefaultSubprocessLogging(argv);
93 gflags::ParseCommandLineFlags(&argc, &argv, true);
94 auto fifo_in = SharedFD::Dup(FLAGS_fifo_in);
95 if (!fifo_in->IsOpen()) {
96 LOG(ERROR) << "Error dupping fd " << FLAGS_fifo_in << ": "
97 << fifo_in->StrError();
98 return 1;
99 }
100 close(FLAGS_fifo_in);
101
102 auto fifo_out = SharedFD::Dup(FLAGS_fifo_out);
103 if (!fifo_out->IsOpen()) {
104 LOG(ERROR) << "Error dupping fd " << FLAGS_fifo_out << ": "
105 << fifo_out->StrError();
106 return 1;
107 }
108 close(FLAGS_fifo_out);
109 SharedFD sock;
110
111 if (FLAGS_data_port >= 0) {
112 sock = OpenSocket(FLAGS_data_port);
113 } else if (!FLAGS_data_path.empty()) {
114 sock = OpenSocket(FLAGS_data_path);
115 } else {
116 LOG(FATAL) << "Need `--data_port` or `--data_path`";
117 }
118
119 auto guest_to_host = std::thread([&]() {
120 while (true) {
121 char buf[FLAGS_buffer_size];
122 auto read = fifo_in->Read(buf, sizeof(buf));
123 if (read < 0) {
124 LOG(WARNING) << "Error reading from guest: " << fifo_in->StrError();
125 sleep(1);
126 continue;
127 }
128 DumpPackets("Read from FIFO", buf, read);
129 while (WriteAll(sock, buf, read) == -1) {
130 LOG(WARNING) << "Failed to write to host socket (will retry): "
131 << sock->StrError();
132 // Wait for the host process to be ready
133 sleep(1);
134 sock = OpenSocket(FLAGS_data_port);
135 }
136 }
137 });
138
139 auto host_to_guest = std::thread([&]() {
140 while (true) {
141 char buf[FLAGS_buffer_size];
142 auto read = sock->Read(buf, sizeof(buf));
143 DumpPackets("Read from socket", buf, read);
144 if (read == -1) {
145 LOG(WARNING) << "Failed to read from host socket (will retry): "
146 << sock->StrError();
147 // Wait for the host process to be ready
148 sleep(1);
149 sock = OpenSocket(FLAGS_data_port);
150 continue;
151 }
152 auto wrote = WriteAll(fifo_out, buf, read);
153 if (wrote < 0) {
154 LOG(WARNING) << "Failed to write to guest: " << fifo_out->StrError();
155 sleep(1);
156 continue;
157 }
158 }
159 });
160 guest_to_host.join();
161 host_to_guest.join();
162
163 return 0;
164 }
165
166 } // namespace
167 } // namespace cuttlefish
168
main(int argc,char ** argv)169 int main(int argc, char** argv) {
170 return cuttlefish::TcpConnectorMain(argc, argv);
171 }
172