aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-02-01 11:42:00 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-02-01 11:42:00 +0200
commit724131b7e03934664621f86df2dc2285ff43dba8 (patch)
tree90902315be4120f2f328782e4aaa6188ec90da55
parentf5bda2db551e38d62a49fa8c579e750060b60d59 (diff)
Add host os_release facility
-rw-r--r--bpkg/host-os-release.cxx121
-rw-r--r--bpkg/host-os-release.hxx59
-rw-r--r--bpkg/host-os-release.test.cxx55
-rw-r--r--bpkg/host-os-release.test.testscript178
4 files changed, 413 insertions, 0 deletions
diff --git a/bpkg/host-os-release.cxx b/bpkg/host-os-release.cxx
new file mode 100644
index 0000000..1a5a980
--- /dev/null
+++ b/bpkg/host-os-release.cxx
@@ -0,0 +1,121 @@
+// file : bpkg/host-os-release.cxx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#include <bpkg/host-os-release.hxx>
+
+#include <libbutl/string-parser.hxx> // parse_quoted()
+
+#include <bpkg/diagnostics.hxx>
+
+using namespace butl;
+
+namespace bpkg
+{
+ // Note: not static for access from the unit test.
+ //
+ os_release
+ host_os_release_linux (path f = {})
+ {
+ os_release r;
+
+ // According to os-release(5), we should use /etc/os-release and fallback
+ // to /usr/lib/os-release if the former does not exist. It also lists the
+ // fallback values for individual variables, in case some are not present.
+ //
+ if (!f.empty ()
+ ? exists (f)
+ : (exists (f = path ("/etc/os-release")) ||
+ exists (f = path ("/usr/lib/os-release"))))
+ {
+ try
+ {
+ ifdstream ifs (f, ifdstream::badbit);
+
+ string l;
+ for (uint64_t ln (1); !eof (getline (ifs, l)); ++ln)
+ {
+ trim (l);
+
+ // Skip blanks lines and comments.
+ //
+ if (l.empty () || l[0] == '#')
+ continue;
+
+ // The variable assignments are in the "shell style" and so can be
+ // quoted/escaped. For now we only handle quoting, which is what all
+ // the instances seen in the wild seems to use.
+ //
+ size_t p (l.find ('='));
+ if (p == string::npos)
+ continue;
+
+ string n (l, 0, p);
+ l.erase (0, p + 1);
+
+ using string_parser::parse_quoted;
+ using string_parser::invalid_string;
+
+ try
+ {
+ if (n == "ID_LIKE")
+ {
+ r.like_ids.clear ();
+
+ vector<string> vs (parse_quoted (l, true /* unquote */));
+ for (const string& v: vs)
+ {
+ for (size_t b (0), e (0); next_word (v, b, e); )
+ {
+ r.like_ids.push_back (string (v, b, e - b));
+ }
+ }
+ }
+ else if (string* p = (n == "ID" ? &r.name_id :
+ n == "VERSION_ID" ? &r.version_id :
+ n == "VARIANT_ID" ? &r.variant_id :
+ n == "NAME" ? &r.name :
+ n == "VERSION_CODENAME" ? &r.version_codename :
+ n == "VARIANT" ? &r.variant :
+ nullptr))
+ {
+ vector<string> vs (parse_quoted (l, true /* unquote */));
+ switch (vs.size ())
+ {
+ case 0: *p = ""; break;
+ case 1: *p = move (vs.front ()); break;
+ default: throw invalid_string (0, "multiple values");
+ }
+ }
+ }
+ catch (const invalid_string& e)
+ {
+ location loc (move (f).string (), ln);
+ fail (loc) << "invalid " << n << " value: " << e;
+ }
+ }
+
+ ifs.close ();
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to read from " << f << ": " << e;
+ }
+ }
+
+ // Assign fallback values.
+ //
+ if (r.name_id.empty ()) r.name_id = "linux";
+ if (r.name.empty ()) r.name = "Linux";
+
+ return r;
+ }
+
+ optional<os_release>
+ host_os_release (const target_triplet& host)
+ {
+ if (host.class_ == "linux")
+ return host_os_release_linux ();
+ else
+ return nullopt;
+ }
+}
diff --git a/bpkg/host-os-release.hxx b/bpkg/host-os-release.hxx
new file mode 100644
index 0000000..3f85cb2
--- /dev/null
+++ b/bpkg/host-os-release.hxx
@@ -0,0 +1,59 @@
+// file : bpkg/host-os-release.hxx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BPKG_HOST_OS_RELEASE_HXX
+#define BPKG_HOST_OS_RELEASE_HXX
+
+#include <bpkg/types.hxx>
+#include <bpkg/utility.hxx>
+
+namespace bpkg
+{
+ // Information extracted from /etc/os-release on Linux. See os-release(5)
+ // for background. For other platforms we derive the equivalent information
+ // from other sources. Some examples:
+ //
+ // {"debian", {}, "10", "",
+ // "Debian GNU/Linux", "buster", ""}
+ //
+ // {"fedora", {}, "35", "workstation",
+ // "Fedora Linux", "", "Workstation Edition"}
+ //
+ // {"ubuntu", {"debian"}, "20.04", "",
+ // "Ubuntu", "focal", ""}
+ //
+ // {"windows", {}, "10", "",
+ // "Windows", "", ""}
+ //
+ // Note that version_id may be empty, for example, on Debian testing:
+ //
+ // {"debian", {}, "", "",
+ // "Debian GNU/Linux", "", ""}
+ //
+ // Note also that we don't extract PRETTY_NAME because its content is
+ // unpredictable. For example, it may include variant, as in "Fedora Linux
+ // 35 (Workstation Edition)". Instead, construct it from the individual
+ // components as appropriate, normally "$name $version ($version_codename)".
+ //
+ struct os_release
+ {
+ string name_id; // ID
+ vector<string> like_ids; // ID_LIKE
+ string version_id; // VERSION_ID
+ string variant_id; // VARIANT_ID
+
+ string name; // NAME
+ string version_codename; // VERSION_CODENAME
+ string variant; // VARIANT
+ };
+
+ // Return the release information for the specified host or nullopt if the
+ // specific host is unknown/unsupported. Note that "host" here implies that
+ // we may be examining files, environment variables, etc., of the machine we
+ // are running on.
+ //
+ optional<os_release>
+ host_os_release (const target_triplet& host);
+}
+
+#endif // BPKG_HOST_OS_RELEASE_HXX
diff --git a/bpkg/host-os-release.test.cxx b/bpkg/host-os-release.test.cxx
new file mode 100644
index 0000000..e65c3ca
--- /dev/null
+++ b/bpkg/host-os-release.test.cxx
@@ -0,0 +1,55 @@
+// file : bpkg/host-os-release.test.cxx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#include <bpkg/host-os-release.hxx>
+
+#include <bpkg/types.hxx>
+#include <bpkg/utility.hxx>
+
+#include <iostream>
+
+#undef NDEBUG
+#include <cassert>
+
+using namespace std;
+
+namespace bpkg
+{
+ extern os_release
+ host_os_release_linux (path f = {});
+
+ int
+ main (int argc, char* argv[])
+ {
+ assert (argc >= 2); // <host-target-triplet>
+
+ target_triplet host (argv[1]);
+
+ os_release r;
+ if (host.class_ == "linux")
+ {
+ assert (argc == 3); // <host-target-triplet> <file-path>
+ r = host_os_release_linux (path (argv[2]));
+ }
+ else
+ assert (false);
+
+ cout << r.name_id << '\n';
+ for (auto b (r.like_ids.begin ()), i (b); i != r.like_ids.end (); ++i)
+ cout << (i != b ? "|" : "") << *i;
+ cout << '\n'
+ << r.version_id << '\n'
+ << r.variant_id << '\n'
+ << r.name << '\n'
+ << r.version_codename << '\n'
+ << r.variant << '\n';
+
+ return 0;
+ }
+}
+
+int
+main (int argc, char* argv[])
+{
+ return bpkg::main (argc, argv);
+}
diff --git a/bpkg/host-os-release.test.testscript b/bpkg/host-os-release.test.testscript
new file mode 100644
index 0000000..b32c477
--- /dev/null
+++ b/bpkg/host-os-release.test.testscript
@@ -0,0 +1,178 @@
+# file : bpkg/host-os-release.test.testscript
+# license : MIT; see accompanying LICENSE file
+
+: linux
+:
+$* x86_64-linux-gnu os-release >>EOO
+ linux
+
+
+
+ Linux
+
+
+ EOO
+
+: debian-10
+:
+cat <<EOI >=os-release;
+ PRETTY_NAME="Debian GNU/Linux 10 (buster)"
+ NAME="Debian GNU/Linux"
+ VERSION_ID="10"
+ VERSION="10 (buster)"
+ VERSION_CODENAME=buster
+ ID=debian
+ HOME_URL="https://www.debian.org/"
+ SUPPORT_URL="https://www.debian.org/support"
+ BUG_REPORT_URL="https://bugs.debian.org/"
+ EOI
+$* x86_64-linux-gnu os-release >>EOO
+ debian
+
+ 10
+
+ Debian GNU/Linux
+ buster
+
+ EOO
+
+: debian-testing
+:
+cat <<EOI >=os-release;
+ PRETTY_NAME="Debian GNU/Linux bookworm/sid"
+ NAME="Debian GNU/Linux"
+ ID=debian
+ HOME_URL="https://www.debian.org/"
+ SUPPORT_URL="https://www.debian.org/support"
+ BUG_REPORT_URL="https://bugs.debian.org/"
+ EOI
+$* x86_64-linux-gnu os-release >>EOO
+ debian
+
+
+
+ Debian GNU/Linux
+
+
+ EOO
+
+: ubuntu-20.04
+:
+cat <<EOI >=os-release;
+ NAME="Ubuntu"
+ VERSION="20.04.1 LTS (Focal Fossa)"
+ ID=ubuntu
+ ID_LIKE=debian
+ PRETTY_NAME="Ubuntu 20.04.1 LTS"
+ VERSION_ID="20.04"
+ HOME_URL="https://www.ubuntu.com/"
+ SUPPORT_URL="https://help.ubuntu.com/"
+ BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
+ PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
+ VERSION_CODENAME=focal
+ UBUNTU_CODENAME=focal
+ EOI
+$* x86_64-linux-gnu os-release >>EOO
+ ubuntu
+ debian
+ 20.04
+
+ Ubuntu
+ focal
+
+ EOO
+
+: fedora-35
+:
+cat <<EOI >=os-release;
+ NAME="Fedora Linux"
+ VERSION="35 (Workstation Edition)"
+ ID=fedora
+ VERSION_ID=35
+ VERSION_CODENAME=""
+ PLATFORM_ID="platform:f35"
+ PRETTY_NAME="Fedora Linux 35 (Workstation Edition)"
+ ANSI_COLOR="0;38;2;60;110;180"
+ LOGO=fedora-logo-icon
+ CPE_NAME="cpe:/o:fedoraproject:fedora:35"
+ HOME_URL="https://fedoraproject.org/"
+ DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f35/system-administrators-guide/"
+ SUPPORT_URL="https://ask.fedoraproject.org/"
+ BUG_REPORT_URL="https://bugzilla.redhat.com/"
+ REDHAT_BUGZILLA_PRODUCT="Fedora"
+ REDHAT_BUGZILLA_PRODUCT_VERSION=35
+ REDHAT_SUPPORT_PRODUCT="Fedora"
+ REDHAT_SUPPORT_PRODUCT_VERSION=35
+ PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"
+ VARIANT="Workstation Edition"
+ VARIANT_ID=workstation
+ EOI
+$* x86_64-linux-gnu os-release >>EOO
+ fedora
+
+ 35
+ workstation
+ Fedora Linux
+
+ Workstation Edition
+ EOO
+
+: rhel-8.2
+:
+cat <<EOI >=os-release;
+ NAME="Red Hat Enterprise Linux"
+ VERSION="8.2 (Ootpa)"
+ ID="rhel"
+ ID_LIKE="fedora"
+ VERSION_ID="8.2"
+ PLATFORM_ID="platform:el8"
+ PRETTY_NAME="Red Hat Enterprise Linux 8.2 (Ootpa)"
+ ANSI_COLOR="0;31"
+ CPE_NAME="cpe:/o:redhat:enterprise_linux:8.2:GA"
+ HOME_URL="https://www.redhat.com/"
+ BUG_REPORT_URL="https://bugzilla.redhat.com/"
+
+ REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8"
+ REDHAT_BUGZILLA_PRODUCT_VERSION=8.2
+ REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"
+ REDHAT_SUPPORT_PRODUCT_VERSION="8.2"
+ EOI
+$* x86_64-linux-gnu os-release >>EOO
+ rhel
+ fedora
+ 8.2
+
+ Red Hat Enterprise Linux
+
+
+ EOO
+
+: centos-8
+:
+cat <<EOI >=os-release;
+ NAME="CentOS Linux"
+ VERSION="8 (Core)"
+ ID="centos"
+ ID_LIKE="rhel fedora"
+ VERSION_ID="8"
+ PLATFORM_ID="platform:el8"
+ PRETTY_NAME="CentOS Linux 8 (Core)"
+ ANSI_COLOR="0;31"
+ CPE_NAME="cpe:/o:centos:centos:8"
+ HOME_URL="https://www.centos.org/"
+ BUG_REPORT_URL="https://bugs.centos.org/"
+
+ CENTOS_MANTISBT_PROJECT="CentOS-8"
+ CENTOS_MANTISBT_PROJECT_VERSION="8"
+ REDHAT_SUPPORT_PRODUCT="centos"
+ REDHAT_SUPPORT_PRODUCT_VERSION="8"
+ EOI
+$* x86_64-linux-gnu os-release >>EOO
+ centos
+ rhel|fedora
+ 8
+
+ CentOS Linux
+
+
+ EOO