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