189 lines
6.1 KiB
C++
189 lines
6.1 KiB
C++
/*
|
|
* 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_lock.h"
|
|
|
|
#include <sys/file.h>
|
|
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/strings.h>
|
|
#include <fruit/fruit.h>
|
|
|
|
#include "common/libs/fs/shared_fd.h"
|
|
#include "common/libs/utils/environment.h"
|
|
#include "common/libs/utils/files.h"
|
|
#include "common/libs/utils/result.h"
|
|
|
|
namespace cuttlefish {
|
|
|
|
InstanceLockFile::InstanceLockFile(SharedFD fd, int instance_num)
|
|
: fd_(fd), instance_num_(instance_num) {}
|
|
|
|
int InstanceLockFile::Instance() const { return instance_num_; }
|
|
|
|
Result<InUseState> InstanceLockFile::Status() const {
|
|
CF_EXPECT(fd_->LSeek(0, SEEK_SET) == 0, fd_->StrError());
|
|
char state_char = static_cast<char>(InUseState::kNotInUse);
|
|
CF_EXPECT(fd_->Read(&state_char, 1) >= 0, fd_->StrError());
|
|
switch (state_char) {
|
|
case static_cast<char>(InUseState::kInUse):
|
|
return InUseState::kInUse;
|
|
case static_cast<char>(InUseState::kNotInUse):
|
|
return InUseState::kNotInUse;
|
|
default:
|
|
return CF_ERR("Unexpected state value \"" << state_char << "\"");
|
|
}
|
|
}
|
|
|
|
Result<void> InstanceLockFile::Status(InUseState state) {
|
|
CF_EXPECT(fd_->LSeek(0, SEEK_SET) == 0, fd_->StrError());
|
|
char state_char = static_cast<char>(state);
|
|
CF_EXPECT(fd_->Write(&state_char, 1) == 1, fd_->StrError());
|
|
return {};
|
|
}
|
|
|
|
bool InstanceLockFile::operator<(const InstanceLockFile& other) const {
|
|
if (instance_num_ != other.instance_num_) {
|
|
return instance_num_ < other.instance_num_;
|
|
}
|
|
return fd_ < other.fd_;
|
|
}
|
|
|
|
InstanceLockFileManager::InstanceLockFileManager() = default;
|
|
|
|
// Replicates tempfile.gettempdir() in Python
|
|
std::string TempDir() {
|
|
std::vector<std::string> try_dirs = {
|
|
StringFromEnv("TMPDIR", ""),
|
|
StringFromEnv("TEMP", ""),
|
|
StringFromEnv("TMP", ""),
|
|
"/tmp",
|
|
"/var/tmp",
|
|
"/usr/tmp",
|
|
};
|
|
for (const auto& try_dir : try_dirs) {
|
|
if (DirectoryExists(try_dir)) {
|
|
return try_dir;
|
|
}
|
|
}
|
|
return CurrentDirectory();
|
|
}
|
|
|
|
static Result<SharedFD> OpenLockFile(int instance_num) {
|
|
std::stringstream path;
|
|
path << TempDir() << "/acloud_cvd_temp/";
|
|
CF_EXPECT(EnsureDirectoryExists(path.str()));
|
|
path << "local-instance-" << instance_num << ".lock";
|
|
auto fd = SharedFD::Open(path.str(), O_CREAT | O_RDWR, 0666);
|
|
CF_EXPECT(fd->IsOpen(), "open(\"" << path.str() << "\"): " << fd->StrError());
|
|
return fd;
|
|
}
|
|
|
|
Result<InstanceLockFile> InstanceLockFileManager::AcquireLock(
|
|
int instance_num) {
|
|
auto fd = CF_EXPECT(OpenLockFile(instance_num));
|
|
CF_EXPECT(fd->Flock(LOCK_EX), fd->StrError());
|
|
return InstanceLockFile(fd, instance_num);
|
|
}
|
|
|
|
Result<std::set<InstanceLockFile>> InstanceLockFileManager::AcquireLocks(
|
|
const std::set<int>& instance_nums) {
|
|
std::set<InstanceLockFile> locks;
|
|
for (const auto& num : instance_nums) {
|
|
locks.emplace(CF_EXPECT(AcquireLock(num)));
|
|
}
|
|
return locks;
|
|
}
|
|
|
|
Result<std::optional<InstanceLockFile>> InstanceLockFileManager::TryAcquireLock(
|
|
int instance_num) {
|
|
auto fd = CF_EXPECT(OpenLockFile(instance_num));
|
|
int flock_result = fd->Flock(LOCK_EX | LOCK_NB);
|
|
if (flock_result == 0) {
|
|
return InstanceLockFile(fd, instance_num);
|
|
} else if (flock_result == -1 && fd->GetErrno() == EWOULDBLOCK) {
|
|
return {};
|
|
}
|
|
return CF_ERR("flock " << instance_num << " failed: " << fd->StrError());
|
|
}
|
|
|
|
Result<std::set<InstanceLockFile>> InstanceLockFileManager::TryAcquireLocks(
|
|
const std::set<int>& instance_nums) {
|
|
std::set<InstanceLockFile> locks;
|
|
for (const auto& num : instance_nums) {
|
|
auto lock = CF_EXPECT(TryAcquireLock(num));
|
|
if (lock) {
|
|
locks.emplace(std::move(*lock));
|
|
}
|
|
}
|
|
return locks;
|
|
}
|
|
|
|
static Result<std::set<int>> AllInstanceNums() {
|
|
// Estimate this by looking at available tap devices
|
|
// clang-format off
|
|
/** Sample format:
|
|
Inter-| Receive | Transmit
|
|
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
|
cvd-wtap-02: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
*/
|
|
// clang-format on
|
|
static constexpr char kPath[] = "/proc/net/dev";
|
|
std::string proc_net_dev;
|
|
using android::base::ReadFileToString;
|
|
CF_EXPECT(ReadFileToString(kPath, &proc_net_dev, /* follow_symlinks */ true));
|
|
auto lines = android::base::Split(proc_net_dev, "\n");
|
|
std::set<int> etaps, mtaps, wtaps;
|
|
for (const auto& line : lines) {
|
|
std::set<int>* tap_set = nullptr;
|
|
if (android::base::StartsWith(line, "cvd-etap-")) {
|
|
tap_set = &etaps;
|
|
} else if (android::base::StartsWith(line, "cvd-mtap-")) {
|
|
tap_set = &mtaps;
|
|
} else if (android::base::StartsWith(line, "cvd-wtap-")) {
|
|
tap_set = &wtaps;
|
|
} else {
|
|
continue;
|
|
}
|
|
tap_set->insert(std::stoi(line.substr(std::string{"cvd-etap-"}.size())));
|
|
}
|
|
std::set<int> emtaps;
|
|
std::set_intersection(etaps.begin(), etaps.end(), mtaps.begin(), mtaps.end(),
|
|
std::inserter(emtaps, emtaps.begin()));
|
|
std::set<int> emwtaps;
|
|
std::set_intersection(emtaps.begin(), emtaps.end(), wtaps.begin(),
|
|
wtaps.end(), std::inserter(emwtaps, emwtaps.begin()));
|
|
return emwtaps;
|
|
}
|
|
|
|
Result<std::optional<InstanceLockFile>>
|
|
InstanceLockFileManager::TryAcquireUnusedLock() {
|
|
auto nums = CF_EXPECT(AllInstanceNums());
|
|
for (const auto& num : nums) {
|
|
auto lock = CF_EXPECT(TryAcquireLock(num));
|
|
if (lock && CF_EXPECT(lock->Status()) == InUseState::kNotInUse) {
|
|
return std::move(*lock);
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
} // namespace cuttlefish
|