blob: 45f5afce617b0a3acd58b4cbe155a53efbf8e12d [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 <iostream>
#include <optional>
#include <string>
#include <vector>
#include <android-base/logging.h>
#include <android-base/scopeguard.h>
#include <fmt/core.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 "host/commands/snapshot_util_cvd/parse.h"
#include "host/commands/snapshot_util_cvd/snapshot_taker.h"
#include "host/libs/command_util/runner/proto_utils.h"
#include "host/libs/command_util/util.h"
#include "host/libs/config/cuttlefish_config.h"
#include "run_cvd.pb.h"
namespace cuttlefish {
namespace {
Result<RequestInfo> SerializeRequest(const SnapshotCmd subcmd,
const std::string& meta_json_path) {
switch (subcmd) {
case SnapshotCmd::kSuspend: {
return RequestInfo{
.serialized_data = CF_EXPECT(SerializeSuspendRequest()),
.extended_action_type = ExtendedActionType::kSuspend,
};
break;
}
case SnapshotCmd::kResume: {
return RequestInfo{
.serialized_data = CF_EXPECT(SerializeResumeRequest()),
.extended_action_type = ExtendedActionType::kResume,
};
break;
}
case SnapshotCmd::kSnapshotTake: {
return RequestInfo{
.serialized_data =
CF_EXPECT(SerializeSnapshotTakeRequest(meta_json_path)),
.extended_action_type = ExtendedActionType::kSnapshotTake,
};
break;
}
default:
return CF_ERR("Operation not supported.");
}
}
Result<std::string> ToAbsolutePath(const std::string& snapshot_path) {
const InputPathForm default_path_form{
.current_working_dir = std::nullopt,
.home_dir = std::nullopt,
.path_to_convert = snapshot_path,
.follow_symlink = false,
};
return CF_EXPECTF(
EmulateAbsolutePath(default_path_form),
"The snapshot path, \"{}\", cannot be converted to an absolute path",
snapshot_path);
}
static void OnSnapshotTakeFailure(const std::string& snapshot_path) {
if (snapshot_path.empty()) {
return;
}
LOG(DEBUG) << "Deleting " << snapshot_path << "....";
RecursivelyRemoveDirectory(snapshot_path);
}
Result<void> SnapshotCvdMain(std::vector<std::string> args) {
CF_EXPECT(!args.empty(), "No arguments was given");
const auto prog_path = args.front();
args.erase(args.begin());
auto parsed = CF_EXPECT(Parse(args));
if (!parsed.snapshot_path.empty()) {
parsed.snapshot_path = CF_EXPECT(ToAbsolutePath(parsed.snapshot_path));
}
// make sure the snapshot directory exists
std::string meta_json_path;
if (parsed.cmd == SnapshotCmd::kSnapshotTake) {
CF_EXPECT(!parsed.snapshot_path.empty(),
"Snapshot operation requires snapshot path.");
CF_EXPECTF(!FileExists(parsed.snapshot_path, /* follow symlink */ false),
"Delete the destination directiory \"{}\" first",
parsed.snapshot_path);
android::base::ScopeGuard delete_snapshot_on_fail([&parsed]() {
if (!parsed.cleanup_snapshot_path) {
return;
}
LOG(ERROR) << "Snapshot take failed, so running clean-up.";
OnSnapshotTakeFailure(parsed.snapshot_path);
});
if (!parsed.cleanup_snapshot_path) {
delete_snapshot_on_fail.Disable();
}
meta_json_path =
CF_EXPECT(HandleHostGroupSnapshot(parsed.snapshot_path),
"Failed to back up the group-level host runtime files.");
delete_snapshot_on_fail.Disable();
}
const CuttlefishConfig* config =
CF_EXPECT(CuttlefishConfig::Get(), "Failed to obtain config object");
// TODO(kwstephenkim): copy host files that are shared by the instance group
for (const auto instance_num : parsed.instance_nums) {
SharedFD monitor_socket = CF_EXPECT(
GetLauncherMonitor(*config, instance_num, parsed.wait_for_launcher));
LOG(INFO) << "Requesting " << parsed.cmd << " for instance #"
<< instance_num;
android::base::ScopeGuard delete_snapshot_on_fail([&parsed]() {
LOG(ERROR) << "Snapshot take failed, so running clean-up.";
OnSnapshotTakeFailure(parsed.snapshot_path);
});
if (parsed.cmd != SnapshotCmd::kSnapshotTake) {
delete_snapshot_on_fail.Disable();
}
auto [serialized_data, extended_type] =
CF_EXPECT(SerializeRequest(parsed.cmd, meta_json_path));
CF_EXPECT(
WriteLauncherActionWithData(monitor_socket, LauncherAction::kExtended,
extended_type, std::move(serialized_data)));
LOG(INFO) << "Wrote the extended serialized data and reading response";
LauncherResponse response = CF_EXPECT(ReadLauncherResponse(monitor_socket));
LOG(INFO) << "Read the response: " << (int)LauncherResponse::kSuccess;
CF_EXPECTF(response == LauncherResponse::kSuccess,
"Received \"{}\" response from launcher monitor for \""
"{}\" request.",
static_cast<char>(response), static_cast<int>(parsed.cmd));
LOG(INFO) << parsed.cmd << " was successful for instance #" << instance_num;
if (parsed.cmd == SnapshotCmd::kSnapshotTake) {
delete_snapshot_on_fail.Disable();
}
}
return {};
}
} // namespace
} // namespace cuttlefish
int main(int argc, char** argv) {
::android::base::InitLogging(argv, android::base::StderrLogger);
std::vector<std::string> all_args = cuttlefish::ArgsToVec(argc, argv);
auto result = cuttlefish::SnapshotCvdMain(std::move(all_args));
if (!result.ok()) {
LOG(ERROR) << result.error().FormatForEnv();
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}