blob: d6fdf246543fa88e72f10c6d17064c394ab945b3 [file] [log] [blame]
// Copyright 2018 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.
#ifndef CRASH_REPORTER_CRASH_SENDER_UTIL_H_
#define CRASH_REPORTER_CRASH_SENDER_UTIL_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <base/files/file_path.h>
#include <base/files/scoped_temp_dir.h>
#include <brillo/key_value_store.h>
#include <metrics/metrics_library.h>
#include <session_manager/dbus-proxies.h>
namespace util {
// Maximum crashes to send per 24 hours.
constexpr int kMaxCrashRate = 32;
// Maximum time to wait for ensuring a meta file is complete.
constexpr int kMaxHoldOffTimeInSeconds = 30;
// Maximum time to sleep before attempting to send a crash report. This value is
// inclusive as an upper bound, thus 0 means a crash report can be sent
// immediately.
constexpr int kMaxSpreadTimeInSeconds = 600;
// Represents a name-value pair for an environment variable.
struct EnvPair {
const char* name;
const char* value;
};
// Parsed command line flags.
struct CommandLineFlags {
base::TimeDelta max_spread_time;
};
// Crash information obtained in ChooseAction().
struct CrashInfo {
brillo::KeyValueStore metadata;
base::FilePath payload_file;
std::string payload_kind;
};
// Details of a crash report. Contains more information than CrashInfo, as
// additional information is extracted at a stage later stage.
struct CrashDetails {
base::FilePath meta_file;
base::FilePath payload_file;
std::string payload_kind;
std::string exec_name;
};
// Represents a metadata file name, and its parsed metadata.
typedef std::pair<base::FilePath, std::unique_ptr<CrashInfo>> MetaFile;
// Actions returned by ChooseAction().
enum Action {
kRemove, // Should remove the crash report.
kIgnore, // Should ignore (keep) the crash report.
kSend, // Should send the crash report.
};
// Predefined environment variables for controlling the behaviors of
// crash_sender.
//
// TODO(satorux): Remove the environment variables once the shell script is
// gone. The environment variables are handy in the shell script, but should not
// be needed in the C++ version.
constexpr EnvPair kEnvironmentVariables[] = {
// Set this to 1 in the environment to allow uploading crash reports
// for unofficial versions.
{"FORCE_OFFICIAL", "0"},
// Set this to 1 in the environment to pretend to have booted in developer
// mode. This is used by autotests.
{"MOCK_DEVELOPER_MODE", "0"},
// Ignore PAUSE_CRASH_SENDING file if set.
{"OVERRIDE_PAUSE_SENDING", "0"},
};
// Parses the command line, and handles the command line flags.
//
// This function also sets the predefined environment valuables to the default
// values, or the values specified by -e options.
//
// On error, the process exits as a failure with an error message for the
// first-encountered error.
void ParseCommandLine(int argc,
const char* const* argv,
CommandLineFlags* flags);
// Returns true if mock is enabled.
bool IsMock();
// Returns true if the sending should be paused.
bool ShouldPauseSending();
// Checks if the dependencies used in the shell script exist. On error, returns
// false, and saves the first path that was missing in |missing_path|.
// TODO(satorux): Remove this once rewriting to C++ is complete.
bool CheckDependencies(base::FilePath* missing_path);
// Gets the base part of a crash report file, such as name.01234.5678.9012 from
// name.01234.5678.9012.meta or name.01234.5678.9012.log.tar.xz. We make sure
// "name" is sanitized in CrashCollector::Sanitize to not include any periods.
// The directory part will be preserved.
base::FilePath GetBasePartOfCrashFile(const base::FilePath& file_name);
// Removes orphaned files in |crash_dir|, that are files 24 hours old or older,
// without corresponding meta file.
void RemoveOrphanedCrashFiles(const base::FilePath& crash_dir);
// Chooses an action to take for the crash report associated with the given meta
// file, and reports the reason. |metrics_lib| is used to check if metrics are
// enabled, etc. The crash information will be stored in |info| for reuse.
Action ChooseAction(const base::FilePath& meta_file,
MetricsLibraryInterface* metrics_lib,
std::string* reason,
CrashInfo* info);
// Removes invalid files in |crash_dir|, that are unknown, corrupted, or invalid
// in other ways, and picks crash reports that should be sent to the server. The
// meta files of the latter will be stored in |to_send|. See ChooseAction() for
// |metrics_lib|.
void RemoveAndPickCrashFiles(const base::FilePath& crash_dir,
MetricsLibraryInterface* metrics_lib,
std::vector<MetaFile>* to_send);
// Removes report files associated with the given meta file.
// More specifically, if "foo.meta" is given, "foo.*" will be removed.
void RemoveReportFiles(const base::FilePath& meta_file);
// Returns the list of meta data files (files with ".meta" suffix), sorted by
// the timestamp in the old-to-new order.
std::vector<base::FilePath> GetMetaFiles(const base::FilePath& crash_dir);
// Gets the base name of the path pointed by |key| in the given metadata.
// Returns an empty path if the key is not found.
base::FilePath GetBaseNameFromMetadata(const brillo::KeyValueStore& metadata,
const std::string& key);
// Returns which kind of report from the given payload path. Returns an empty
// string if the kind is unknown.
std::string GetKindFromPayloadPath(const base::FilePath& payload_path);
// Parses |raw_metadata| into |metadata|. Keys in metadata are validated (keys
// should consist of expected characters). Returns true on success.
// The original contents of |metadata| will be lost.
bool ParseMetadata(const std::string& raw_metadata,
brillo::KeyValueStore* metadata);
// Returns true if the metadata is complete.
bool IsCompleteMetadata(const brillo::KeyValueStore& metadata);
// Returns true if the given timestamp file is new enough, indicating that there
// was a recent attempt to send a crash report.
bool IsTimestampNewEnough(const base::FilePath& timestamp_file);
// Returns true if sending a crash report now does not exceed |max_crash_rate|
// per 24 hours.. Reports the current rate (# of reports sent in the past 24
// hours) in |current_rate| regardless of the return value.
//
// This function checks/creates/removes timestamp files in |timestamps_dir| to
// track how many attempts were made to send crash reports in that past 24
// hours. Even if sending failed, it's counted as an attempt.
bool IsBelowRate(const base::FilePath& timestamps_dir,
int max_crash_rate,
int* current_rate);
// Computes a sleep time needed before attempting to send a new crash report.
// On success, returns true and stores the result in |sleep_time|. On error,
// returns false.
bool GetSleepTime(const base::FilePath& meta_file,
const base::TimeDelta& max_spread_time,
base::TimeDelta* sleep_time);
// Returns the value for the key from the key-value store, or "undefined", if
// the key is not found ("undefined" will be used in crash reports where the
// values are missing).
std::string GetValueOrUndefined(const brillo::KeyValueStore& store,
const std::string& key);
// A helper class for sending crashes. The behaviors can be customized with
// Options class for unit testing.
//
// Crash reports will be sent even when the device is on a mobile data
// connection (see crbug.com/185110 for discussion).
class Sender {
public:
struct Options {
// The shell script used for sending crashes.
base::FilePath shell_script = base::FilePath("/sbin/crash_sender.sh");
// Session manager client for locating the user-specific crash directories.
org::chromium::SessionManagerInterfaceProxyInterface* proxy = nullptr;
// Maximum crashes to send per 24 hours.
int max_crash_rate = kMaxCrashRate;
// Maximum time to sleep before attempting to send.
base::TimeDelta max_spread_time;
// Alternate sleep function for unit testing.
base::Callback<void(base::TimeDelta)> sleep_function;
};
Sender(std::unique_ptr<MetricsLibraryInterface> metrics_lib,
const Options& options);
// Initializes the sender object. Returns true on success.
bool Init();
// Sends crashes in |crash_dir|, in multiple steps:
//
// Before sending:
// - Removes crash files that are orphaned or invalid.
// - Removes all crash files if crash reporting is not enabled.
//
// While sending:
// - Checks if the device enters guest mode, and stops if entered.
// - TODO(satorux): The followings are done in the shell script.
// - Enforces the rate limit per 24 hours.
// - Removes crash files that are successfully uploaded.
//
// Returns true if no error occurred, or |crash_dir| does not exist.
bool SendCrashes(const base::FilePath& crash_dir);
// Sends the user-specific crashes. This function calls SendCrashes() for each
// user specific crash directory. Returns true if no error occurred.
bool SendUserCrashes();
// Returns the temporary directory used in the object. Valid after Init() is
// completed successfully.
const base::FilePath& temp_dir() const { return scoped_temp_dir_.GetPath(); }
private:
// Requests the shell script to send a crash report represented with the given
// crash details.
bool RequestToSendCrash(const CrashDetails& details);
std::unique_ptr<MetricsLibraryInterface> metrics_lib_;
const base::FilePath shell_script_;
std::unique_ptr<org::chromium::SessionManagerInterfaceProxyInterface> proxy_;
base::ScopedTempDir scoped_temp_dir_;
const int max_crash_rate_;
const base::TimeDelta max_spread_time_;
base::Callback<void(base::TimeDelta)> sleep_function_;
DISALLOW_COPY_AND_ASSIGN(Sender);
};
} // namespace util
#endif // CRASH_REPORTER_CRASH_SENDER_UTIL_H_