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