1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
11 #include <string_view>
13 #include "base/process_util.h"
14 #include "mozilla/FileUtils.h"
15 #include "mozilla/Result.h"
16 #include "mozilla/ResultVariant.h"
17 #include "mozilla/ipc/LaunchError.h"
19 namespace mozilla::widget::lsb
{
21 static const char gLsbReleasePath
[] = "/usr/bin/lsb_release";
22 static const char gEtcOsReleasePath
[] = "/etc/os-release";
23 static const char gUsrOsReleasePath
[] = "/usr/lib/os-release";
25 // See https://www.freedesktop.org/software/systemd/man/latest/os-release.html
26 bool ExtractAndSetValue(nsACString
& aContainer
, std::string_view
& aValue
) {
27 // We assume the value is well formed and doesn't contain escape characters.
28 if (aValue
.size() > 1 && (aValue
.front() == '"' || aValue
.front() == '\'')) {
29 // We assume the quote is properly balanced.
30 aValue
= aValue
.substr(1, aValue
.size() - 2);
32 aContainer
.Assign(aValue
.data(), aValue
.size());
33 return !aValue
.empty();
36 bool GetOSRelease(nsACString
& aDistributor
, nsACString
& aDescription
,
37 nsACString
& aRelease
, nsACString
& aCodename
) {
38 std::ifstream
stream(gEtcOsReleasePath
);
40 stream
.open(gUsrOsReleasePath
);
45 bool seen_id
= false, seen_pretty_name
= false, seen_version_id
= false;
48 while (std::getline(stream
, rawline
)) {
49 std::string_view
line(rawline
);
50 size_t pos
= line
.find('=');
51 if (pos
!= std::string_view::npos
) {
52 auto key
= line
.substr(0, pos
);
53 auto value
= line
.substr(pos
+ 1);
55 if (ExtractAndSetValue(aDistributor
, value
)) {
56 // Capitalize the first letter of the id. This mimics what
57 // lsb_release does on Debian and derivatives. On RH derivatives,
58 // ID tends to be capitalized already.
59 char* c
= aDistributor
.BeginWriting();
60 if (*c
>= 'a' && *c
<= 'z') {
65 } else if (key
== "NAME") {
66 ExtractAndSetValue(name
, value
);
67 } else if (key
== "PRETTY_NAME") {
68 if (ExtractAndSetValue(aDescription
, value
)) seen_pretty_name
= true;
69 } else if (key
== "VERSION_ID") {
70 if (ExtractAndSetValue(aRelease
, value
)) seen_version_id
= true;
71 } else if (key
== "VERSION_CODENAME") {
72 ExtractAndSetValue(aCodename
, value
);
76 // If NAME is set and only differs from ID in case, use NAME.
77 if (seen_id
&& !name
.IsEmpty() && name
.EqualsIgnoreCase(aDistributor
)) {
80 // Only consider our work done if we've seen at least ID, PRETTY_NAME and
82 return seen_id
&& seen_pretty_name
&& seen_version_id
;
85 bool GetLSBRelease(nsACString
& aDistributor
, nsACString
& aDescription
,
86 nsACString
& aRelease
, nsACString
& aCodename
) {
87 // Nowadays, /etc/os-release is more likely to be available than
88 // /usr/bin/lsb_release. Relying on the former also avoids forking.
89 if (GetOSRelease(aDistributor
, aDescription
, aRelease
, aCodename
)) {
93 if (access(gLsbReleasePath
, R_OK
) != 0) return false;
96 if (pipe(pipefd
) == -1) {
97 NS_WARNING("pipe() failed!");
101 std::vector
<std::string
> argv
= {gLsbReleasePath
, "-idrc"};
103 base::LaunchOptions options
;
104 options
.fds_to_remap
.push_back({pipefd
[1], STDOUT_FILENO
});
107 base::ProcessHandle process
;
108 Result
<Ok
, ipc::LaunchError
> err
=
109 base::LaunchApp(argv
, std::move(options
), &process
);
112 NS_WARNING("Failed to spawn lsb_release!");
117 ScopedCloseFile
stream(fdopen(pipefd
[0], "r"));
119 NS_WARNING("Could not wrap fd!");
124 char dist
[256], desc
[256], release
[256], codename
[256];
125 if (fscanf(stream
.get(),
126 "Distributor ID:\t%255[^\n]\n"
127 "Description:\t%255[^\n]\n"
128 "Release:\t%255[^\n]\n"
129 "Codename:\t%255[^\n]\n",
130 dist
, desc
, release
, codename
) != 4) {
131 NS_WARNING("Failed to parse lsb_release!");
135 aDistributor
.Assign(dist
);
136 aDescription
.Assign(desc
);
137 aRelease
.Assign(release
);
138 aCodename
.Assign(codename
);
142 } // namespace mozilla::widget::lsb