chromium/chromecast/crash/linux/synchronized_minidump_manager.h

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROMECAST_CRASH_LINUX_SYNCHRONIZED_MINIDUMP_MANAGER_H_
#define CHROMECAST_CRASH_LINUX_SYNCHRONIZED_MINIDUMP_MANAGER_H_

#include <optional>
#include <vector>

#include "base/files/file_path.h"
#include "base/values.h"
#include "chromecast/crash/linux/dump_info.h"

namespace chromecast {

// Abstract base class for mutually-exclusive minidump handling. Ensures
// synchronized access among instances of this class to the minidumps directory
// using a file lock. The "lockfile" also holds serialized metadata about each
// of the minidumps in the directory. Derived classes should not access the
// lockfile directly. Instead, use protected methods to query and modify the
// metadata, but only within the implementation of DoWork().
//
// This class holds an in memory representation of the lockfile and metadata
// file when the lockfile is held. Modifier methods work on this in memory
// representation. When the lockfile is released, the in memory representations
// are written to file
//
// The lockfile file is of the following format:
// { <dump_info1> }
// { <dump_info2> }
// ...
// { <dump_infoN> }
//
// Note that this isn't a valid json object. It is formatted in this way so
// that producers to this file do not need to understand json.
//
// Current external producers:
// + watchdog
//
//
// The metadata file is a separate file containing a json dictionary.
//
class SynchronizedMinidumpManager {
 public:
  // Length of a ratelimit period in seconds.
  static const int kRatelimitPeriodSeconds;

  // Number of dumps allowed per period.
  static const int kRatelimitPeriodMaxDumps;

  SynchronizedMinidumpManager(const SynchronizedMinidumpManager&) = delete;
  SynchronizedMinidumpManager& operator=(const SynchronizedMinidumpManager&) =
      delete;

  virtual ~SynchronizedMinidumpManager();

 protected:
  SynchronizedMinidumpManager();

  // Acquires the lock, calls DoWork(), then releases the lock when DoWork()
  // returns. Derived classes should expose a method which calls this. Returns
  // the status of DoWork(), or false if the lock was not successfully acquired.
  bool AcquireLockAndDoWork();

  // Derived classes must implement this method. It will be called from
  // DoWorkLocked after the lock has been successfully acquired. The lockfile
  // shall be accessed and mutated only through the methods below. All other
  // files shall be managed as needed by the derived class.
  virtual bool DoWork() = 0;

  // Get the current dumps in the lockfile.
  std::vector<std::unique_ptr<DumpInfo>> GetDumps();

  // Set |dumps| as the dumps in |lockfile_|, replacing current list of dumps.
  bool SetCurrentDumps(const std::vector<std::unique_ptr<DumpInfo>>& dumps);

  // Serialize |dump_info| and append it to the lockfile. Note that the child
  // class must only call this inside DoWork(). This should be the only method
  // used to write to the lockfile. Only call this if the minidump has been
  // generated in the minidumps directory successfully. Returns true on success,
  // false otherwise.
  bool AddEntryToLockFile(const DumpInfo& dump_info);

  // Remove the lockfile entry at |index| in the current in memory
  // representation of the lockfile. If the index is invalid returns false,
  // otherwise returns true.
  bool RemoveEntryFromLockFile(int index);

  // Get the number of un-uploaded dumps in the dump_path directory.
  // If delete_all_dumps is true, also delete all these files, this is used to
  // clean lingering dump files.
  int GetNumDumps(bool delete_all_dumps);

  // Increment the number of dumps in the current ratelimit period.
  // Returns true on success, false on error.
  bool IncrementNumDumpsInCurrentPeriod();

  // Decrement the number of dumps in the current ratelimit period.
  // Returns true on success, false on error.
  bool DecrementNumDumpsInCurrentPeriod();

  // Start a new rate-limit period, thus allowing crash uploads to proceed.
  void ResetRateLimitPeriod();

  // Returns true when dumps uploaded in current rate limit period is less than
  // |kRatelimitPeriodMaxDumps|. Resets rate limit period if period time has
  // elapsed.
  bool CanUploadDump();

  // Returns true when there are dumps in the lockfile or extra files in the
  // dump directory, false otherwise.
  // Used to avoid unnecessary file locks in consumers.
  bool HasDumps();

  // Ensures that the lockfile and metadata are in a valid state. This requires
  // obtaining the lockfile. Will fail if lockfile already held.
  bool InitializeFileState();

  // Cached path for the minidumps directory.
  const base::FilePath dump_path_;

 private:
  // Acquire the lock file. Blocks if another process holds it, or if called
  // a second time by the same process. Returns true if successful, or false
  // otherwise.
  bool AcquireLockFile();

  // Parse the lockfile and metadata file, populating |dumps_| and |metadata_|
  // for modifier functions to use. Returns false if an error occurred,
  // otherwise returns true. This must not be called unless |this| has acquired
  // the lock.
  bool ParseFiles();

  // Write deserialized |dumps| to |lockfile_path_| and the deserialized
  // |metadata| to |metadata_path_|.
  bool WriteFiles(const base::Value::List& dumps,
                  const base::Value::Dict& metadata);

  // Creates an empty lock file and an initialized metadata file.
  bool InitializeFiles();

  // Release the lock file with the associated *fd*.
  void ReleaseLockFile();

  const base::FilePath lockfile_path_;
  const base::FilePath metadata_path_;
  int lockfile_fd_;
  std::optional<base::Value::Dict> metadata_;
  std::optional<base::Value::List> dumps_;
};

}  // namespace chromecast

#endif  // CHROMECAST_CRASH_LINUX_SYNCHRONIZED_MINIDUMP_MANAGER_H_