blob: 7ea1b74451ac08a4f2976f6343494db1a84d288c [file] [log] [blame]
/*
* Copyright (C) 2022 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/instance_manager.h"
#include <map>
#include <mutex>
#include <optional>
#include <thread>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <fruit/fruit.h>
#include "cvd_server.pb.h"
#include "common/libs/fs/shared_buf.h"
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/files.h"
#include "common/libs/utils/flag_parser.h"
#include "common/libs/utils/result.h"
#include "common/libs/utils/subprocess.h"
#include "host/commands/cvd/server_constants.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/config/known_paths.h"
namespace cuttlefish {
std::optional<std::string> GetCuttlefishConfigPath(const std::string& home) {
std::string home_realpath;
if (DirectoryExists(home)) {
CHECK(android::base::Realpath(home, &home_realpath));
static const char kSuffix[] = "/cuttlefish_assembly/cuttlefish_config.json";
std::string config_path = AbsolutePath(home_realpath + kSuffix);
if (FileExists(config_path)) {
return config_path;
}
}
return {};
}
InstanceManager::InstanceManager(InstanceLockFileManager& lock_manager)
: lock_manager_(lock_manager) {}
bool InstanceManager::HasInstanceGroups() const {
std::lock_guard lock(instance_groups_mutex_);
return !instance_groups_.empty();
}
void InstanceManager::SetInstanceGroup(
const InstanceManager::InstanceGroupDir& dir,
const InstanceManager::InstanceGroupInfo& info) {
std::lock_guard assemblies_lock(instance_groups_mutex_);
instance_groups_[dir] = info;
}
void InstanceManager::RemoveInstanceGroup(
const InstanceManager::InstanceGroupDir& dir) {
std::lock_guard assemblies_lock(instance_groups_mutex_);
instance_groups_.erase(dir);
}
Result<InstanceManager::InstanceGroupInfo> InstanceManager::GetInstanceGroup(
const InstanceManager::InstanceGroupDir& dir) const {
std::lock_guard assemblies_lock(instance_groups_mutex_);
auto info_it = instance_groups_.find(dir);
if (info_it == instance_groups_.end()) {
return CF_ERR("No group dir \"" << dir << "\"");
} else {
return info_it->second;
}
}
cvd::Status InstanceManager::CvdFleet(const SharedFD& out,
const std::string& env_config) const {
std::lock_guard assemblies_lock(instance_groups_mutex_);
const char _GroupDeviceInfoStart[] = "[\n";
const char _GroupDeviceInfoSeparate[] = ",\n";
const char _GroupDeviceInfoEnd[] = "]\n";
WriteAll(out, _GroupDeviceInfoStart);
for (const auto& [group_dir, group_info] : instance_groups_) {
auto config_path = GetCuttlefishConfigPath(group_dir);
if (FileExists(env_config)) {
config_path = env_config;
}
if (config_path) {
// Reads CuttlefishConfig::instance_names(), which must remain stable
// across changes to config file format (within server_constants.h major
// version).
auto config = CuttlefishConfig::GetFromFile(*config_path);
if (config) {
Command command(group_info.host_binaries_dir + kStatusBin);
command.AddParameter("--print");
command.AddParameter("--all_instances");
command.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, out);
command.AddEnvironmentVariable(kCuttlefishConfigEnvVarName,
*config_path);
if (int wait_result = command.Start().Wait(); wait_result != 0) {
WriteAll(out, " (unknown instance status error)");
}
}
}
if (group_dir != instance_groups_.rbegin()->first) {
WriteAll(out, _GroupDeviceInfoSeparate);
}
}
WriteAll(out, _GroupDeviceInfoEnd);
cvd::Status status;
status.set_code(cvd::Status::OK);
return status;
}
cvd::Status InstanceManager::CvdClear(const SharedFD& out,
const SharedFD& err) {
std::lock_guard lock(instance_groups_mutex_);
cvd::Status status;
for (const auto& [group_dir, group_info] : instance_groups_) {
auto config_path = GetCuttlefishConfigPath(group_dir);
if (config_path) {
// Stop all instances that are using this group dir.
Command command(group_info.host_binaries_dir + kStopBin);
// Delete the instance dirs.
command.AddParameter("--clear_instance_dirs");
command.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, out);
command.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, err);
command.AddEnvironmentVariable(kCuttlefishConfigEnvVarName, *config_path);
if (int wait_result = command.Start().Wait(); wait_result != 0) {
WriteAll(
out,
"Warning: error stopping instances for dir \"" + group_dir +
"\".\nThis can happen if instances are already stopped.\n");
}
for (const auto& instance : group_info.instances) {
auto lock = lock_manager_.TryAcquireLock(instance);
if (lock.ok() && (*lock)) {
(*lock)->Status(InUseState::kNotInUse);
}
}
}
}
RemoveFile(StringFromEnv("HOME", ".") + "/cuttlefish_runtime");
RemoveFile(GetGlobalConfigFileLink());
WriteAll(out, "Stopped all known instances\n");
instance_groups_.clear();
status.set_code(cvd::Status::OK);
return status;
}
} // namespace cuttlefish