1 /*
2 * Copyright (C) 2019 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 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android-base/logging.h>
18 #include <fmt/format.h>
19
20 #include <chrono>
21 #include <fstream>
22
23 #include "common/libs/fs/shared_fd.h"
24 #include "common/libs/fs/shared_select.h"
25 #include "common/libs/utils/flag_parser.h"
26 #include "common/libs/utils/shared_fd_flag.h"
27 #include "host/libs/config/logging.h"
28
29 namespace cuttlefish {
30
31 static uint num_tombstones_in_last_second = 0;
32 static std::string last_tombstone_name = "";
33
next_tombstone_path(const std::string & dir)34 static std::string next_tombstone_path(const std::string& dir) {
35 auto in_time = std::chrono::system_clock::now();
36 auto retval = fmt::format("{}/tombstone_{:%Y-%m-%d-%H%M%S}", dir, in_time);
37
38 // Gives tombstones unique names
39 if(retval == last_tombstone_name) {
40 num_tombstones_in_last_second++;
41 retval += "_" + std::to_string(num_tombstones_in_last_second);
42 } else {
43 last_tombstone_name = retval;
44 num_tombstones_in_last_second = 0;
45 }
46
47 LOG(DEBUG) << "Creating " << retval;
48 return retval;
49 }
50
51 static constexpr size_t CHUNK_RECV_MAX_LEN = 1024;
52 static constexpr size_t TIMEOUT_SEC = 3;
53
TombstoneReceiverMain(int argc,char ** argv)54 int TombstoneReceiverMain(int argc, char** argv) {
55 DefaultSubprocessLogging(argv);
56
57 std::vector<Flag> flags;
58
59 std::string tombstone_dir;
60 flags.emplace_back(GflagsCompatFlag("tombstone_dir", tombstone_dir)
61 .Help("directory to write out tombstones in"));
62
63 SharedFD server_fd;
64 flags.emplace_back(
65 SharedFDFlag("server_fd", server_fd)
66 .Help("File descriptor to an already created vsock server"));
67
68 flags.emplace_back(HelpFlag(flags));
69 flags.emplace_back(UnexpectedArgumentGuard());
70
71 std::vector<std::string> args =
72 ArgsToVec(argc - 1, argv + 1); // Skip argv[0]
73 auto parse_res = ConsumeFlags(flags, args);
74 CHECK(parse_res.ok()) << "Could not process command line flags. "
75 << parse_res.error().FormatForEnv();
76
77 CHECK(server_fd->IsOpen()) << "Did not receive a server fd";
78
79 LOG(DEBUG) << "Host is starting server on port "
80 << server_fd->VsockServerPort();
81
82 // Server loop
83 while (true) {
84 auto conn = SharedFD::Accept(*server_fd);
85 std::ofstream file(next_tombstone_path(tombstone_dir),
86 std::ofstream::out | std::ofstream::binary);
87 auto acc = 0;
88 auto cnt = 0;
89
90 SharedFDSet read_set;
91 read_set.Set(conn);
92
93 SharedFDSet error_set;
94 error_set.Set(conn);
95 while (file.is_open()) {
96 struct timeval timeout = {TIMEOUT_SEC, 0};
97 auto val = Select(&read_set, nullptr, &error_set, &timeout);
98 if (val == 0) {
99 LOG(DEBUG) << "timeout";
100 break;
101 }
102 if (read_set.IsSet(conn)) {
103 char buff[CHUNK_RECV_MAX_LEN];
104 auto bytes_read = conn->Recv(buff, sizeof(buff), 0);
105 acc += bytes_read;
106 if (bytes_read <= 0) {
107 // reset the other side if it's still connected
108 break;
109 } else {
110 file.write(buff, bytes_read);
111 cnt++;
112 }
113 }
114
115 if (error_set.IsSet(conn)) {
116 LOG(DEBUG) << "error";
117 break;
118 }
119 }
120 LOG(DEBUG) << "done: " << acc << " bytes via " << cnt;
121 }
122
123 return 0;
124 }
125
126 } // namespace cuttlefish
127
main(int argc,char ** argv)128 int main(int argc, char** argv) {
129 return cuttlefish::TombstoneReceiverMain(argc, argv);
130 }
131