blob: e2716a7f4729352054102c3f7eef2238c9deea51 [file] [log] [blame]
// Copyright 2021 The Crashpad Authors
//
// 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 "client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h"
#include <utility>
#include "client/prune_crash_reports.h"
#include "util/file/directory_reader.h"
#include "util/file/filesystem.h"
#include "util/ios/scoped_background_task.h"
namespace crashpad {
namespace {
// The file extension used to indicate a file is locked.
constexpr char kLockedExtension[] = ".locked";
// Prune onces a day.
constexpr time_t prune_interval = 60 * 60 * 24;
// If the client finds a locked file matching it's own bundle id, unlock it
// after 24 hours.
constexpr time_t matching_bundle_locked_ttl = 60 * 60 * 24;
// Unlock any locked intermediate dump after 60 days.
constexpr time_t max_locked_ttl = 60 * 60 * 24 * 60;
// The initial thread delay for applications. Delay the thread's file i/o to
// not interfere with application startup.
constexpr double app_delay = 60;
// The initial thread delay for app extensions. Because iOS extensions are often
// very short lived, do not wait the full |app_delay|, and instead use a shorter
// time.
constexpr double extension_delay = 5;
//! \brief Unlocks old intermediate dumps.
//!
//! This function can unlock (remove the .locked extension) intermediate dumps
//! that are either too old to be useful, or are likely leftover dumps from
//! clean app exits.
//!
//! \param[in] pending_path The path to any locked intermediate dump files.
//! \param[in] bundle_identifier_and_seperator The identifier for this client,
//! used to determine when locked files are considered stale.
void UnlockOldIntermediateDumps(base::FilePath pending_path,
std::string bundle_identifier_and_seperator) {
DirectoryReader reader;
std::vector<base::FilePath> files;
if (!reader.Open(pending_path)) {
return;
}
base::FilePath file;
DirectoryReader::Result result;
while ((result = reader.NextFile(&file)) ==
DirectoryReader::Result::kSuccess) {
if (file.FinalExtension() != kLockedExtension)
continue;
const base::FilePath file_path(pending_path.Append(file));
timespec file_time;
time_t now = time(nullptr);
if (!FileModificationTime(file_path, &file_time)) {
continue;
}
if ((file.value().compare(0,
bundle_identifier_and_seperator.size(),
bundle_identifier_and_seperator) == 0 &&
file_time.tv_sec <= now - matching_bundle_locked_ttl) ||
(file_time.tv_sec <= now - max_locked_ttl)) {
base::FilePath new_path = file_path.RemoveFinalExtension();
MoveFileOrDirectory(file_path, new_path);
continue;
}
}
}
} // namespace
PruneIntermediateDumpsAndCrashReportsThread::
PruneIntermediateDumpsAndCrashReportsThread(
CrashReportDatabase* database,
std::unique_ptr<PruneCondition> condition,
base::FilePath pending_path,
std::string bundle_identifier_and_seperator,
bool is_extension)
: thread_(prune_interval, this),
condition_(std::move(condition)),
pending_path_(pending_path),
bundle_identifier_and_seperator_(bundle_identifier_and_seperator),
initial_work_delay_(is_extension ? extension_delay : app_delay),
last_start_time_(0),
database_(database) {}
PruneIntermediateDumpsAndCrashReportsThread::
~PruneIntermediateDumpsAndCrashReportsThread() {}
void PruneIntermediateDumpsAndCrashReportsThread::Start() {
thread_.Start(initial_work_delay_);
}
void PruneIntermediateDumpsAndCrashReportsThread::Stop() {
thread_.Stop();
}
void PruneIntermediateDumpsAndCrashReportsThread::DoWork(
const WorkerThread* thread) {
// This thread may be stopped and started a number of times throughout the
// lifetime of the process to prevent 0xdead10cc kills (see
// crbug.com/crashpad/400), but it should only run once per prune_interval
// after initial_work_delay_.
time_t now = time(nullptr);
if (now - last_start_time_ < prune_interval)
return;
last_start_time_ = now;
internal::ScopedBackgroundTask scoper("PruneThread");
database_->CleanDatabase(60 * 60 * 24 * 3);
// Here and below, respect Stop() being called after each task.
if (!thread_.is_running())
return;
PruneCrashReportDatabase(database_, condition_.get());
if (!thread_.is_running())
return;
if (!clean_old_intermediate_dumps_) {
clean_old_intermediate_dumps_ = true;
UnlockOldIntermediateDumps(pending_path_, bundle_identifier_and_seperator_);
}
}
} // namespace crashpad