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 "host/commands/cvd/instance_manager.h"
18
19 #include <map>
20 #include <mutex>
21 #include <optional>
22 #include <thread>
23
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26 #include <fruit/fruit.h>
27
28 #include "cvd_server.pb.h"
29
30 #include "common/libs/fs/shared_buf.h"
31 #include "common/libs/fs/shared_fd.h"
32 #include "common/libs/utils/files.h"
33 #include "common/libs/utils/flag_parser.h"
34 #include "common/libs/utils/result.h"
35 #include "common/libs/utils/subprocess.h"
36 #include "host/commands/cvd/server_constants.h"
37 #include "host/libs/config/cuttlefish_config.h"
38 #include "host/libs/config/known_paths.h"
39
40 namespace cuttlefish {
41
GetCuttlefishConfigPath(const std::string & home)42 std::optional<std::string> GetCuttlefishConfigPath(const std::string& home) {
43 std::string home_realpath;
44 if (DirectoryExists(home)) {
45 CHECK(android::base::Realpath(home, &home_realpath));
46 static const char kSuffix[] = "/cuttlefish_assembly/cuttlefish_config.json";
47 std::string config_path = AbsolutePath(home_realpath + kSuffix);
48 if (FileExists(config_path)) {
49 return config_path;
50 }
51 }
52 return {};
53 }
54
InstanceManager(InstanceLockFileManager & lock_manager)55 InstanceManager::InstanceManager(InstanceLockFileManager& lock_manager)
56 : lock_manager_(lock_manager) {}
57
HasInstanceGroups() const58 bool InstanceManager::HasInstanceGroups() const {
59 std::lock_guard lock(instance_groups_mutex_);
60 return !instance_groups_.empty();
61 }
62
SetInstanceGroup(const InstanceManager::InstanceGroupDir & dir,const InstanceManager::InstanceGroupInfo & info)63 void InstanceManager::SetInstanceGroup(
64 const InstanceManager::InstanceGroupDir& dir,
65 const InstanceManager::InstanceGroupInfo& info) {
66 std::lock_guard assemblies_lock(instance_groups_mutex_);
67 instance_groups_[dir] = info;
68 }
69
RemoveInstanceGroup(const InstanceManager::InstanceGroupDir & dir)70 void InstanceManager::RemoveInstanceGroup(
71 const InstanceManager::InstanceGroupDir& dir) {
72 std::lock_guard assemblies_lock(instance_groups_mutex_);
73 instance_groups_.erase(dir);
74 }
75
GetInstanceGroup(const InstanceManager::InstanceGroupDir & dir) const76 Result<InstanceManager::InstanceGroupInfo> InstanceManager::GetInstanceGroup(
77 const InstanceManager::InstanceGroupDir& dir) const {
78 std::lock_guard assemblies_lock(instance_groups_mutex_);
79 auto info_it = instance_groups_.find(dir);
80 if (info_it == instance_groups_.end()) {
81 return CF_ERR("No group dir \"" << dir << "\"");
82 } else {
83 return info_it->second;
84 }
85 }
86
CvdFleet(const SharedFD & out,const std::string & env_config) const87 cvd::Status InstanceManager::CvdFleet(const SharedFD& out,
88 const std::string& env_config) const {
89 std::lock_guard assemblies_lock(instance_groups_mutex_);
90 const char _GroupDeviceInfoStart[] = "[\n";
91 const char _GroupDeviceInfoSeparate[] = ",\n";
92 const char _GroupDeviceInfoEnd[] = "]\n";
93 WriteAll(out, _GroupDeviceInfoStart);
94 for (const auto& [group_dir, group_info] : instance_groups_) {
95 auto config_path = GetCuttlefishConfigPath(group_dir);
96 if (FileExists(env_config)) {
97 config_path = env_config;
98 }
99 if (config_path) {
100 // Reads CuttlefishConfig::instance_names(), which must remain stable
101 // across changes to config file format (within server_constants.h major
102 // version).
103 auto config = CuttlefishConfig::GetFromFile(*config_path);
104 if (config) {
105 Command command(group_info.host_binaries_dir + kStatusBin);
106 command.AddParameter("--print");
107 command.AddParameter("--all_instances");
108 command.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, out);
109 command.AddEnvironmentVariable(kCuttlefishConfigEnvVarName,
110 *config_path);
111 if (int wait_result = command.Start().Wait(); wait_result != 0) {
112 WriteAll(out, " (unknown instance status error)");
113 }
114 }
115 }
116 if (group_dir != instance_groups_.rbegin()->first) {
117 WriteAll(out, _GroupDeviceInfoSeparate);
118 }
119 }
120 WriteAll(out, _GroupDeviceInfoEnd);
121 cvd::Status status;
122 status.set_code(cvd::Status::OK);
123 return status;
124 }
125
CvdClear(const SharedFD & out,const SharedFD & err)126 cvd::Status InstanceManager::CvdClear(const SharedFD& out,
127 const SharedFD& err) {
128 std::lock_guard lock(instance_groups_mutex_);
129 cvd::Status status;
130 for (const auto& [group_dir, group_info] : instance_groups_) {
131 auto config_path = GetCuttlefishConfigPath(group_dir);
132 if (config_path) {
133 // Stop all instances that are using this group dir.
134 Command command(group_info.host_binaries_dir + kStopBin);
135 // Delete the instance dirs.
136 command.AddParameter("--clear_instance_dirs");
137 command.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, out);
138 command.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, err);
139 command.AddEnvironmentVariable(kCuttlefishConfigEnvVarName, *config_path);
140 if (int wait_result = command.Start().Wait(); wait_result != 0) {
141 WriteAll(
142 out,
143 "Warning: error stopping instances for dir \"" + group_dir +
144 "\".\nThis can happen if instances are already stopped.\n");
145 }
146 for (const auto& instance : group_info.instances) {
147 auto lock = lock_manager_.TryAcquireLock(instance);
148 if (lock.ok() && (*lock)) {
149 (*lock)->Status(InUseState::kNotInUse);
150 }
151 }
152 }
153 }
154 RemoveFile(StringFromEnv("HOME", ".") + "/cuttlefish_runtime");
155 RemoveFile(GetGlobalConfigFileLink());
156 WriteAll(out, "Stopped all known instances\n");
157
158 instance_groups_.clear();
159 status.set_code(cvd::Status::OK);
160 return status;
161 }
162
163 } // namespace cuttlefish
164