diff --git a/libdnf/repo/Repo-private.hpp b/libdnf/repo/Repo-private.hpp index 1f659e6fb..88cadf7d6 100644 --- a/libdnf/repo/Repo-private.hpp +++ b/libdnf/repo/Repo-private.hpp @@ -91,6 +91,7 @@ class Repo::Impl { void fetch(const std::string & destdir, std::unique_ptr && h); std::string getCachedir() const; std::string getPersistdir() const; + time_t getSystemEpoch() const; int getAge() const; void expire(); bool isExpired() const; diff --git a/libdnf/repo/Repo.cpp b/libdnf/repo/Repo.cpp index 40b0b68ec..ead986189 100644 --- a/libdnf/repo/Repo.cpp +++ b/libdnf/repo/Repo.cpp @@ -900,7 +900,7 @@ void Repo::Impl::addCountmeFlag(LrHandle *handle) { // Load the cookie std::string fname = getPersistdir() + "/" + COUNTME_COOKIE; int ver = COUNTME_VERSION; // file format version (for future use) - time_t epoch = 0; // position of first-ever counted window + time_t epoch = 0; // position of first observed window time_t win = COUNTME_OFFSET; // position of last counted window int budget = -1; // budget for this window (-1 = generate) std::ifstream(fname) >> ver >> epoch >> win >> budget; @@ -926,8 +926,15 @@ void Repo::Impl::addCountmeFlag(LrHandle *handle) { // Compute the position of this window win = now - (delta % COUNTME_WINDOW); + + // Compute the epoch from this system's epoch or, if unknown, declare + // this window as the epoch (unless stored in the cookie previously). + time_t sysepoch = getSystemEpoch(); + if (sysepoch) + epoch = sysepoch - ((sysepoch - COUNTME_OFFSET) % COUNTME_WINDOW); if (!epoch) epoch = win; + // Window step (0 at epoch) int step = (win - epoch) / COUNTME_WINDOW; @@ -1221,6 +1228,31 @@ std::string Repo::Impl::getPersistdir() const return result; } +/* Returns this system's installation time ("epoch") as a UNIX timestamp. + * + * Uses the machine-id(5) file's mtime as a good-enough source of truth. This + * file is typically tied to the system's installation or first boot where it's + * populated by an installer tool or init system, respectively, and is never + * changed afterwards. + * + * Some systems, such as containers that don't run an init system, may have the + * file missing, empty or uninitialized, in which case this function returns 0. + */ +time_t Repo::Impl::getSystemEpoch() const +{ + std::string filename = "/etc/machine-id"; + std::string id; + struct stat st; + + if (stat(filename.c_str(), &st) != 0 || !st.st_size) + return 0; + std::ifstream(filename) >> id; + if (id == "uninitialized") + return 0; + + return st.st_mtime; +} + int Repo::Impl::getAge() const { return time(NULL) - mtime(getMetadataPath(MD_TYPE_PRIMARY).c_str());