blob: c0fb090416f77cbde718ac861ca33c6b7d61b901 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <set>
#include <string>
#include <base/files/file_path.h>
#include <brillo/errors/error.h>
#include <chromeos/dbus/service_constants.h>
#include "dlcservice/error.h"
#include "dlcservice/ref_count.h"
#include "dlcservice/system_state.h"
#include "dlcservice/utils.h"
using std::set;
using std::string;
using std::unique_ptr;
namespace dlcservice {
const char kRefCountFileName[] = "ref_count.bin";
const char kSessionStarted[] = "started";
const char kUsedByUser[] = "user";
const char kUsedBySystem[] = "system";
const int kDefaultExpirationDelayDays = 5;
const char kSystemUsername[] = "system";
// static
std::unique_ptr<RefCountInterface> RefCountInterface::Create(
const string& used_by, const base::FilePath& prefs_path) {
if (used_by == "user") {
return std::make_unique<UserRefCount>(prefs_path);
} else if (used_by == "system") {
return std::make_unique<SystemRefCount>(prefs_path);
} else {
NOTREACHED() << "Invalid 'used_by' attribute in manifest: " << used_by;
}
return nullptr;
}
RefCountBase::RefCountBase(const base::FilePath& prefs_path) {
// Load the ref count proto only if it exists.
ref_count_path_ = prefs_path.Append(kRefCountFileName);
if (base::PathExists(ref_count_path_)) {
string ref_count_str;
if (base::ReadFileToString(ref_count_path_, &ref_count_str))
ref_count_info_.ParseFromString(ref_count_str);
}
}
bool RefCountBase::InstalledDlc() {
string username = GetCurrentUserName();
if (username.empty()) {
// Probably no user has logged in. So discard.
return true;
}
// If we already have the user, ignore.
if (std::any_of(ref_count_info_.users().begin(),
ref_count_info_.users().end(),
[&username](const RefCountInfo::User& user) {
return user.sanitized_username() == username;
})) {
return true;
}
// Add the current user to the list of users for this DLC.
ref_count_info_.add_users()->set_sanitized_username(username);
return Persist();
}
bool RefCountBase::UninstalledDlc() {
string username = GetCurrentUserName();
if (username.empty()) {
// Probably no user has logged in. So discard.
return true;
}
// If we don't have the user, ignore.
auto user_it = std::find_if(ref_count_info_.users().begin(),
ref_count_info_.users().end(),
[&username](const RefCountInfo::User& user) {
return user.sanitized_username() == username;
});
if (user_it == ref_count_info_.users().end())
return true;
// Remove the user from the list of users currently using this DLC.
ref_count_info_.mutable_users()->erase(user_it);
return Persist();
}
bool RefCountBase::ShouldPurgeDlc() const {
// If someone is using it, it should not be removed.
if (ref_count_info_.users_size() != 0) {
return false;
}
// If the last access time has not been set, then we don't know the timeline
// and this DLC should not be removed.
int64_t last_access_time_us = ref_count_info_.last_access_time_us();
if (last_access_time_us == 0) {
return false;
}
base::Time last_accessed = base::Time::FromDeltaSinceWindowsEpoch(
base::TimeDelta::FromMicroseconds(last_access_time_us));
base::TimeDelta delta_time = base::Time::Now() - last_accessed;
return delta_time > GetExpirationDelay();
}
bool RefCountBase::Persist() {
ref_count_info_.set_last_access_time_us(
base::Time::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
string ref_count_str;
if (!ref_count_info_.SerializeToString(&ref_count_str)) {
LOG(ERROR) << "Failed to serialize user based ref count proto.";
return false;
}
if (!WriteToFile(ref_count_path_, ref_count_str)) {
PLOG(ERROR) << "Failed to write user based ref count proto to: "
<< ref_count_path_;
return false;
}
return true;
}
// UserRefCount implementations.
set<string> UserRefCount::user_names_;
unique_ptr<string> UserRefCount::primary_session_username_;
// static
void UserRefCount::SessionChanged(const string& state) {
if (state == kSessionStarted) {
user_names_ = ScanDirectory(SystemState::Get()->users_dir());
string username, sanitized_username;
brillo::ErrorPtr err;
if (!SystemState::Get()->session_manager()->RetrievePrimarySession(
&username, &sanitized_username, &err)) {
LOG(ERROR) << "Failed to get the primary session's username with error: "
<< Error::ToString(err);
primary_session_username_.release();
return;
}
primary_session_username_ = std::make_unique<string>(sanitized_username);
}
}
} // namespace dlcservice