blob: e1f4c5aaefb23c5cc0d723b0467e5118c147410b [file] [log] [blame]
// Copyright 2021 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 "croslog/log_rotator/log_rotator.h"
#include <algorithm>
#include <string>
#include <vector>
#include <base/files/file_enumerator.h>
#include <base/files/file_util.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include "croslog/constants.h"
namespace log_rotator {
constexpr int DAYS_TO_PRESERVE_LOGS = 7;
LogRotator::LogRotator(base::FilePath base_log_path)
: base_log_path_(base_log_path) {}
// Creating a new log file for Forwarder is handled outside this code.
base::FilePath LogRotator::GetFilePathWithIndex(int index) {
CHECK_GE(index, 0);
if (index == 0)
return base_log_path_;
return base_log_path_.InsertBeforeExtensionASCII("." +
base::NumberToString(index));
}
int LogRotator::GetIndexFromFilePath(const base::FilePath& log_path) {
if (log_path == base_log_path_)
return 0;
if (log_path.value().length() <= base_log_path_.value().length()) {
// Invalid input, since the given path must be longer than the base path.
return -1;
}
// Verify the base part (before the index number) of the path.
const base::FilePath base_path_body = base_log_path_.RemoveFinalExtension();
size_t base_path_body_len = base_path_body.value().length();
const std::string& log_path_body =
log_path.value().substr(0, base_path_body_len);
if (log_path_body != base_path_body.value()) {
// Invalid input, since the path str doesn't start with the path string
return -1;
}
if (log_path.value()[base_path_body_len] != '.') {
// Invalid input, since in the path, the dot doesn't follow with the base
// path.
return -1;
}
// Verify the final extension of the path.
const std::string& final_extension = base_log_path_.FinalExtension();
size_t final_extension_len = final_extension.length();
const std::string& log_path_final_extension = log_path.value().substr(
log_path.value().length() - final_extension_len, final_extension_len);
if (log_path_final_extension != final_extension) {
// Invalid input, since the path str doesn't start with the path string
return -1;
}
// Extract the index number.
const std::string& log_path_index_str = log_path.value().substr(
base_path_body_len + 1,
log_path.value().length() - base_path_body_len - final_extension_len - 1);
int index = 0;
if (!base::StringToInt(log_path_index_str, &index))
return -1;
if (index == 0) {
// The ".0" extension is invalid. The 0-th file should have no extension.
return -1;
}
return index;
}
void LogRotator::CleanUpFiles(int max_index) {
base::FilePath dir = base_log_path_.DirName();
base::FilePath pattern = base_log_path_.BaseName().AddExtension("*");
base::FileEnumerator file_enumerator(dir, false, base::FileEnumerator::FILES,
pattern.value());
while (!file_enumerator.Next().empty()) {
base::FilePath file_path = dir.Append(file_enumerator.GetInfo().GetName());
int index = GetIndexFromFilePath(file_path);
if (index > max_index || index < 0)
base::DeleteFile(file_path);
}
}
void LogRotator::RotateLogFile(int max_index) {
std::vector<base::FileEnumerator::FileInfo> info;
base::FilePath max_index_path = GetFilePathWithIndex(max_index);
if (base::PathExists(max_index_path))
base::DeleteFile(max_index_path);
for (int i = (max_index - 1); i >= 0; --i) {
base::FilePath old_path = GetFilePathWithIndex(i);
base::FilePath new_path = GetFilePathWithIndex(i + 1);
if (!base::PathExists(old_path))
continue;
if (!base::Move(old_path, new_path)) {
LOG(ERROR) << "File error while moving " << old_path << " to " << new_path
<< ": "
<< base::File::ErrorToString(base::File::GetLastFileError());
}
}
CleanUpFiles(max_index);
}
void RotateStandardLogFiles() {
for (const auto& base_log_path_str : croslog::kLogsToRotate) {
const base::FilePath& base_log_path =
base::FilePath{base_log_path_str.data()};
LogRotator rotator(base_log_path);
rotator.RotateLogFile(DAYS_TO_PRESERVE_LOGS);
}
}
} // namespace log_rotator