blob: 34f4a620a4987b2f8212cdac66fa9ce92a956430 [file] [log] [blame] [edit]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "host/commands/cvd/reset_client_utils.h"
#include <signal.h>
#include <algorithm>
#include <cctype>
#include <iomanip> // std::setw
#include <iostream> // std::endl
#include <regex>
#include <sstream>
#include <unordered_set>
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <fmt/core.h>
#include "common/libs/utils/contains.h"
#include "common/libs/utils/environment.h"
#include "common/libs/utils/files.h"
#include "common/libs/utils/proc_file_utils.h"
#include "common/libs/utils/subprocess.h"
#include "host/commands/cvd/common_utils.h"
#include "host/commands/cvd/reset_client_utils.h"
#include "host/commands/cvd/run_cvd_proc_collector.h"
#include "host/commands/cvd/run_server.h"
#include "host/libs/config/config_constants.h"
namespace cuttlefish {
Result<RunCvdProcessManager> RunCvdProcessManager::Get() {
RunCvdProcessCollector run_cvd_collector =
CF_EXPECT(RunCvdProcessCollector::Get());
RunCvdProcessManager run_cvd_processes_manager(std::move(run_cvd_collector));
return run_cvd_processes_manager;
}
RunCvdProcessManager::RunCvdProcessManager(RunCvdProcessCollector&& collector)
: run_cvd_process_collector_(std::move(collector)) {}
static Command CreateStopCvdCommand(const std::string& stopper_path,
const cvd_common::Envs& envs,
const cvd_common::Args& args) {
Command command(cpp_basename(stopper_path));
command.SetExecutable(stopper_path);
for (const auto& arg : args) {
command.AddParameter(arg);
}
for (const auto& [key, value] : envs) {
command.UnsetFromEnvironment(key);
command.AddEnvironmentVariable(key, value);
}
return command;
}
Result<void> RunCvdProcessManager::RunStopCvd(const GroupProcInfo& group_info,
bool clear_runtime_dirs) {
const auto& stopper_path = group_info.stop_cvd_path_;
int ret_code = 0;
cvd_common::Envs stop_cvd_envs;
stop_cvd_envs["HOME"] = group_info.home_;
if (group_info.android_host_out_) {
stop_cvd_envs[kAndroidHostOut] = group_info.android_host_out_.value();
stop_cvd_envs[kAndroidSoongHostOut] = group_info.android_host_out_.value();
} else {
auto android_host_out = StringFromEnv(
kAndroidHostOut,
android::base::Dirname(android::base::GetExecutableDirectory()));
stop_cvd_envs[kAndroidHostOut] = android_host_out;
stop_cvd_envs[kAndroidSoongHostOut] = android_host_out;
}
if (clear_runtime_dirs) {
Command first_stop_cvd = CreateStopCvdCommand(
stopper_path, stop_cvd_envs, {"--clear_instance_dirs=true"});
LOG(ERROR) << "Running HOME=" << stop_cvd_envs.at("HOME") << " "
<< stopper_path << " --clear_instance_dirs=true";
std::string stdout_str;
std::string stderr_str;
ret_code = RunWithManagedStdio(std::move(first_stop_cvd), nullptr,
std::addressof(stdout_str),
std::addressof(stderr_str));
// TODO(kwstephenkim): deletes manually if `stop_cvd --clear_instance_dirs`
// failed.
}
if (!clear_runtime_dirs || ret_code != 0) {
if (clear_runtime_dirs) {
LOG(ERROR) << "Failed to run " << stopper_path
<< " --clear_instance_dirs=true";
LOG(ERROR) << "Perhaps --clear_instance_dirs is not taken.";
LOG(ERROR) << "Trying again without it";
}
Command second_stop_cvd =
CreateStopCvdCommand(stopper_path, stop_cvd_envs, {});
LOG(ERROR) << "Running HOME=" << stop_cvd_envs.at("HOME") << " "
<< stopper_path;
std::string stdout_str;
std::string stderr_str;
ret_code = RunWithManagedStdio(std::move(second_stop_cvd), nullptr,
std::addressof(stdout_str),
std::addressof(stderr_str));
}
if (ret_code != 0) {
std::stringstream error;
error << "HOME=" << group_info.home_
<< group_info.stop_cvd_path_ + " Failed.";
return CF_ERR(error.str());
}
LOG(ERROR) << "\"" << stopper_path << " successfully "
<< "\" stopped instances at HOME=" << group_info.home_;
return {};
}
Result<void> RunCvdProcessManager::RunStopCvdAll(bool cvd_server_children_only,
bool clear_instance_dirs) {
for (const auto& group_info : run_cvd_process_collector_.CfGroups()) {
if (cvd_server_children_only && !group_info.is_cvd_server_started_) {
continue;
}
auto stop_cvd_result = RunStopCvd(group_info, clear_instance_dirs);
if (!stop_cvd_result.ok()) {
LOG(ERROR) << stop_cvd_result.error().FormatForEnv();
continue;
}
}
return {};
}
static bool IsStillRunCvd(const pid_t pid) {
std::string pid_dir = fmt::format("/proc/{}", pid);
if (!FileExists(pid_dir)) {
return false;
}
auto owner_result = OwnerUid(pid);
if (!owner_result.ok() || (getuid() != *owner_result)) {
return false;
}
auto extract_proc_info_result = ExtractProcInfo(pid);
if (!extract_proc_info_result.ok()) {
return false;
}
return (cpp_basename(extract_proc_info_result->actual_exec_path_) ==
"run_cvd");
}
Result<void> RunCvdProcessManager::SendSignal(bool cvd_server_children_only,
const GroupProcInfo& group_info) {
std::vector<pid_t> failed_pids;
if (cvd_server_children_only && !group_info.is_cvd_server_started_) {
return {};
}
for (const auto& [unused, instance] : group_info.instances_) {
for (const auto parent_run_cvd_pid : instance.parent_run_cvd_pids_) {
if (!IsStillRunCvd(parent_run_cvd_pid)) {
continue;
}
if (kill(parent_run_cvd_pid, SIGKILL) == 0) {
LOG(VERBOSE) << "Successfully SIGKILL'ed " << parent_run_cvd_pid;
} else {
failed_pids.push_back(parent_run_cvd_pid);
}
}
}
CF_EXPECTF(failed_pids.empty(),
"Some run_cvd processes were not killed: [{}]",
fmt::join(failed_pids, ", "));
return {};
}
Result<void> RunCvdProcessManager::DeleteLockFile(
bool cvd_server_children_only, const GroupProcInfo& group_info) {
const std::string lock_dir = "/tmp/acloud_cvd_temp";
std::string lock_file_prefix = lock_dir;
lock_file_prefix.append("/local-instance-");
if (cvd_server_children_only && !group_info.is_cvd_server_started_) {
return {};
}
bool all_success = true;
const auto& instances = group_info.instances_;
for (const auto& [id, _] : instances) {
std::stringstream lock_file_path_stream;
lock_file_path_stream << lock_file_prefix << id << ".lock";
auto lock_file_path = lock_file_path_stream.str();
if (FileExists(lock_file_path) && !DirectoryExists(lock_file_path)) {
if (RemoveFile(lock_file_path)) {
LOG(DEBUG) << "Reset the lock file: " << lock_file_path;
} else {
all_success = false;
LOG(ERROR) << "Failed to remove the lock file: " << lock_file_path;
}
}
}
CF_EXPECT(all_success == true);
return {};
}
Result<void> KillAllCuttlefishInstances(const DeviceClearOptions& options) {
RunCvdProcessManager manager = CF_EXPECT(RunCvdProcessManager::Get());
CF_EXPECT(manager.KillAllCuttlefishInstances(options.cvd_server_children_only,
options.clear_instance_dirs));
return {};
}
Result<void> KillCvdServerProcess() {
std::vector<pid_t> self_exe_pids =
CF_EXPECT(CollectPidsByArgv0(kServerExecPath));
if (self_exe_pids.empty()) {
LOG(ERROR) << "cvd server is not running.";
return {};
}
std::vector<pid_t> cvd_server_pids;
/**
* Finds processes whose executable path is kServerExecPath, and
* that is owned by getuid(), and that has the "INTERNAL_server_fd"
* in the arguments list.
*/
for (const auto pid : self_exe_pids) {
auto proc_info_result = ExtractProcInfo(pid);
if (!proc_info_result.ok()) {
LOG(ERROR) << "Failed to extract process info for pid " << pid;
continue;
}
auto owner_uid_result = OwnerUid(pid);
if (!owner_uid_result.ok()) {
LOG(ERROR) << "Failed to find the uid for pid " << pid;
continue;
}
if (getuid() != *owner_uid_result) {
continue;
}
for (const auto& arg : proc_info_result->args_) {
if (Contains(arg, kInternalServerFd)) {
cvd_server_pids.push_back(pid);
break;
}
}
}
if (cvd_server_pids.empty()) {
LOG(ERROR)
<< "Cvd server process is not found. Perhaps, it is not running.";
return {};
}
if (cvd_server_pids.size() > 1) {
LOG(ERROR) << "There are " << cvd_server_pids.size() << " server processes "
<< "running while it should be up to 1.";
}
for (const auto pid : cvd_server_pids) {
auto kill_ret = kill(pid, SIGKILL);
if (kill_ret == 0) {
LOG(ERROR) << "Cvd server process #" << pid << " is killed.";
} else {
LOG(ERROR) << "kill(" << pid << ", SIGKILL) failed.";
}
}
return {};
}
Result<void> RunCvdProcessManager::KillAllCuttlefishInstances(
bool cvd_server_children_only, bool clear_runtime_dirs) {
auto stop_cvd_result =
RunStopCvdAll(cvd_server_children_only, clear_runtime_dirs);
if (!stop_cvd_result.ok()) {
LOG(ERROR) << stop_cvd_result.error().FormatForEnv();
}
for (const auto& group_info : run_cvd_process_collector_.CfGroups()) {
auto result = ForcefullyStopGroup(cvd_server_children_only, group_info);
if (!result.ok()) {
LOG(ERROR) << result.error().FormatForEnv();
}
}
return {};
}
Result<void> RunCvdProcessManager::ForcefullyStopGroup(
bool cvd_server_children_only, const uid_t id) {
auto groups_info = run_cvd_process_collector_.CfGroups();
for (const auto& group_info : groups_info) {
if (!Contains(group_info.instances_, static_cast<unsigned>(id))) {
continue;
}
CF_EXPECT(ForcefullyStopGroup(cvd_server_children_only, group_info));
}
// run_cvd is not created yet as.. ctrl+C was in assembly phase, etc
return {};
}
Result<void> RunCvdProcessManager::ForcefullyStopGroup(
bool cvd_server_children_only, const GroupProcInfo& group) {
if (cvd_server_children_only && !group.is_cvd_server_started_) {
return {};
}
CF_EXPECTF(SendSignal(cvd_server_children_only, group),
"Tried SIGKILL to a group of run_cvd processes rooted at "
"HOME={} but failed",
group.home_);
CF_EXPECTF(DeleteLockFile(cvd_server_children_only, group),
"Tried to delete instance lock file for the group rooted at "
"HOME={} but failed.",
group.home_);
return {};
}
} // namespace cuttlefish