1 /*
2  * Copyright (C) 2022 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 <sys/wait.h>
18 
19 #include <cstdlib>
20 #include <optional>
21 #include <string>
22 #include <string_view>
23 #include <vector>
24 
25 #include <android-base/strings.h>
26 
27 #include "common/libs/utils/contains.h"
28 #include "common/libs/utils/result.h"
29 #include "common/libs/utils/subprocess.h"
30 #include "host/commands/process_restarter/parser.h"
31 #include "host/libs/config/cuttlefish_config.h"
32 #include "host/libs/config/logging.h"
33 
34 namespace cuttlefish {
35 namespace {
36 
ShouldRestartProcess(siginfo_t const & info,const Parser & parsed)37 static bool ShouldRestartProcess(siginfo_t const& info, const Parser& parsed) {
38   if (info.si_code == CLD_DUMPED && parsed.when_dumped) {
39     return true;
40   }
41   if (info.si_code == CLD_KILLED && parsed.when_killed) {
42     return true;
43   }
44   if (info.si_code == CLD_EXITED && parsed.when_exited_with_failure &&
45       info.si_status != 0) {
46     return true;
47   }
48   if (info.si_code == CLD_EXITED &&
49       info.si_status == parsed.when_exited_with_code) {
50     return true;
51   }
52   return false;
53 }
54 
ExecutableShortName(std::string_view short_name)55 std::string_view ExecutableShortName(std::string_view short_name) {
56   auto last_slash = short_name.find_last_of('/');
57   if (last_slash != std::string::npos) {
58     short_name = short_name.substr(last_slash + 1);
59   }
60   return short_name;
61 }
62 
OptionsForExecutable(std::string_view name)63 Result<SubprocessOptions> OptionsForExecutable(std::string_view name) {
64   const auto& config = CF_EXPECT(CuttlefishConfig::Get());
65   auto options = SubprocessOptions().ExitWithParent(true);
66   std::string short_name{ExecutableShortName(name)};
67   if (Contains(config->straced_host_executables(), short_name)) {
68     const auto& instance = config->ForDefaultInstance();
69     options.Strace(instance.PerInstanceLogPath("/strace-" + short_name));
70   }
71   return options;
72 }
73 
RunProcessRestarter(std::vector<std::string> args)74 Result<int> RunProcessRestarter(std::vector<std::string> args) {
75   LOG(VERBOSE) << "process_restarter starting";
76   auto parsed = CF_EXPECT(Parser::ConsumeAndParse(args));
77 
78   // move-assign the remaining args to exec_args
79   std::vector<std::string> exec_args = std::move(args);
80 
81   bool needs_pop = false;
82   if (!parsed.first_time_argument.empty()) {
83     exec_args.push_back(parsed.first_time_argument);
84     needs_pop = true;
85   }
86 
87   for (;;) {
88     CF_EXPECT(!exec_args.empty());
89     LOG(VERBOSE) << "Starting monitored process " << exec_args.front();
90     // The Execute() API and all APIs effectively called by it show the proper
91     // error message using LOG(ERROR).
92     auto options = CF_EXPECT(OptionsForExecutable(exec_args.front()));
93     siginfo_t info =
94         CF_EXPECTF(Execute(exec_args, std::move(options), WEXITED),
95                    "Executing '{}' failed.", fmt::join(exec_args, "' '"));
96 
97     if (needs_pop) {
98       needs_pop = false;
99       exec_args.pop_back();
100     }
101 
102     if (ShouldRestartProcess(info, parsed)) {
103       continue;
104     }
105     if (info.si_code == CLD_EXITED) {
106       return info.si_status;
107     }
108     LOG(ERROR) << "Process exited with unexpected si_code: " << info.si_code;
109     return 1;
110   }
111 }
112 
113 }  // namespace
114 }  // namespace cuttlefish
115 
main(int argc,char ** argv)116 int main(int argc, char** argv) {
117   cuttlefish::DefaultSubprocessLogging(argv);
118   auto result = cuttlefish::RunProcessRestarter(
119       cuttlefish::ArgsToVec(argc - 1, argv + 1));
120   if (!result.ok()) {
121     LOG(DEBUG) << result.error().FormatForEnv();
122     return EXIT_FAILURE;
123   }
124   return result.value();
125 }
126