Skip to content

Commit 1288245

Browse files
committed
platformutils: avoid procstart to get Linux distro info
Try using the /etc/os-release file, for systemd distros, in favour of calling out to `uname` which can add extra overhead in the form of process startup. Direct file I/O and parsing should be faster.
1 parent 39504ac commit 1288245

File tree

1 file changed

+73
-11
lines changed

1 file changed

+73
-11
lines changed

src/shared/Core/PlatformUtils.cs

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Diagnostics;
34
using System.IO;
5+
using System.Linq;
46
using System.Runtime.InteropServices;
57
using GitCredentialManager.Interop.Posix.Native;
68

@@ -349,6 +351,8 @@ private static string GetOSType()
349351
return "Unknown";
350352
}
351353

354+
private static string _linuxDistroVersion;
355+
352356
private static string GetOSVersion(ITrace2 trace2)
353357
{
354358
//
@@ -373,22 +377,80 @@ private static string GetOSVersion(ITrace2 trace2)
373377

374378
if (IsLinux())
375379
{
376-
var psi = new ProcessStartInfo
377-
{
378-
FileName = "uname",
379-
Arguments = "-a",
380-
RedirectStandardOutput = true
381-
};
380+
return _linuxDistroVersion ??= GetLinuxDistroVersion();
382381

383-
using (var uname = new ChildProcess(trace2, psi))
382+
string GetLinuxDistroVersion()
384383
{
385-
uname.Start(Trace2ProcessClass.Other);
386-
uname.Process.WaitForExit();
384+
// Let's first try to get the distribution information from /etc/os-release
385+
// (or /usr/lib/os-release) which is required in systemd distributions.
386+
// https://www.freedesktop.org/software/systemd/man/os-release.html
387+
foreach (string osReleasePath in new[] { "/etc/os-release", "/usr/lib/os-release" })
388+
{
389+
if (!File.Exists(osReleasePath))
390+
{
391+
continue;
392+
}
393+
394+
var props = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
395+
char[] split = { '=' };
396+
string[] lines = File.ReadAllLines(osReleasePath);
397+
foreach (string line in lines)
398+
{
399+
// Each line is a key="value" pair
400+
string[] kvp = line.Split(split, count: 2);
401+
if (kvp.Length != 2)
402+
{
403+
continue;
404+
}
405+
406+
props[kvp[0]] = kvp[1].Trim('"');
407+
}
408+
409+
// Try to get the PRETTY_NAME first which is a user-friendly description
410+
// including the distro name and version.
411+
if (props.TryGetValue("PRETTY_NAME", out string prettyName))
412+
{
413+
return prettyName;
414+
}
415+
416+
// Fall-back to (NAME || ID) + (VERSION || VERSION_ID || VERSION_CODENAME)?
417+
if (props.TryGetValue("NAME", out string distro) ||
418+
props.TryGetValue("ID", out distro))
419+
{
420+
if (props.TryGetValue("VERSION", out string version) ||
421+
props.TryGetValue("VERSION_ID", out version) ||
422+
props.TryGetValue("VERSION_CODENAME", out version))
423+
{
424+
return $"{distro} {version}";
425+
}
426+
427+
// Return just the distro name if we don't have a version
428+
return distro;
429+
}
430+
}
387431

388-
if (uname.ExitCode == 0)
432+
// If we couldn't get the distribution information from /etc/os-release
433+
// (for example if we're running on a non-systemd distribution), then let's
434+
// use `uname -a` to get at least some information.
435+
var psi = new ProcessStartInfo
389436
{
390-
return uname.StandardOutput.ReadToEnd().Trim();
437+
FileName = "uname",
438+
Arguments = "-a",
439+
RedirectStandardOutput = true
440+
};
441+
442+
using (var uname = new ChildProcess(trace2, psi))
443+
{
444+
uname.Start(Trace2ProcessClass.Other);
445+
uname.Process.WaitForExit();
446+
447+
if (uname.ExitCode == 0)
448+
{
449+
return uname.StandardOutput.ReadToEnd().Trim();
450+
}
391451
}
452+
453+
return "Unknown-Linux";
392454
}
393455
}
394456

0 commit comments

Comments
 (0)