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