131 lines
5.1 KiB
Diff
131 lines
5.1 KiB
Diff
From cc95edd15b8a4fc4c381c85735e2f14a1dc0852e Mon Sep 17 00:00:00 2001
|
|
From: Michal Domonkos <mdomonko@redhat.com>
|
|
Date: Wed, 8 May 2024 12:05:21 +0200
|
|
Subject: [PATCH] Fix countme bucket calculation
|
|
|
|
Actually use the system's installation time (if known) as the reference
|
|
point, instead of the first-ever countme event recorded for the given
|
|
repo.
|
|
|
|
This is what the dnf.conf(5) man page always said about the countme
|
|
option, the code just never lived up to that.
|
|
|
|
This makes bucket calculation more accurate:
|
|
|
|
1. System upgrades will no longer reset the bucket to 1 (this used to be
|
|
the case due to a new persistdir being created whenever $releasever
|
|
changed).
|
|
|
|
2. Systems that only reach out to the repos after an initial time period
|
|
after being installed will no longer appear younger than they really
|
|
are.
|
|
|
|
3. Prebuilt OS images that happen to include countme cookies created at
|
|
build time will no longer cause all the instances spawned from those
|
|
images (physical machines, VMs or containers) to appear older than
|
|
they really are.
|
|
|
|
Use the machine-id(5) file's mtime to infer the installation time. This
|
|
file is semantically tied to the system's lifetime since it's typically
|
|
populated at installation time or during the first boot by an installer
|
|
tool or init system, respectively, and remains unchanged.
|
|
|
|
The fact that it's a well-defined file with clear semantics ensures that
|
|
OS images won't accidentally include a prepopulated version of this file
|
|
with a timestamp corresponding to the image build, unlike our own cookie
|
|
files (see point 3 above).
|
|
|
|
In some cases, such as in OCI containers without an init system running,
|
|
the machine-id file may be missing or empty, even though the system is
|
|
still used long-term. To cover those, keep the original, relative epoch
|
|
as a fallback method. System upgrades aren't really a thing for such
|
|
systems so the above point 1 doesn't apply here.
|
|
|
|
Some containers, such as those created by toolbox(1), may also choose to
|
|
bind-mount the host's machine-id file, thus falling into the same bucket
|
|
as their host. Conveniently, that's what we want, since the purpose of
|
|
such containers is to blend with the host as much as possible.
|
|
|
|
Fixes: #1611
|
|
---
|
|
libdnf/repo/Repo-private.hpp | 1 +
|
|
libdnf/repo/Repo.cpp | 34 +++++++++++++++++++++++++++++++++-
|
|
2 files changed, 34 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/libdnf/repo/Repo-private.hpp b/libdnf/repo/Repo-private.hpp
|
|
index 1f659e6f..88cadf7d 100644
|
|
--- a/libdnf/repo/Repo-private.hpp
|
|
+++ b/libdnf/repo/Repo-private.hpp
|
|
@@ -91,6 +91,7 @@ public:
|
|
void fetch(const std::string & destdir, std::unique_ptr<LrHandle> && 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 40b0b68e..ead98618 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());
|
|
--
|
|
2.33.0
|
|
|