Files
sms-task-two/src/Sms.Environment.Linux/LinuxEnvironmentVariableStore.cs

178 lines
5.4 KiB
C#

namespace Sms.Environment.Linux;
public sealed class LinuxEnvironmentVariableStore : IEnvironmentVariableStore
{
private const string ManagedFileName = "99-sms-task-two.conf";
private readonly string _managedFilePath;
private readonly string _environmentDirectory;
public LinuxEnvironmentVariableStore()
: this(
Path.Combine(
System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile),
".config",
"environment.d"),
ManagedFileName)
{
}
internal LinuxEnvironmentVariableStore(string environmentDirectory, string managedFileName)
{
_environmentDirectory = environmentDirectory;
_managedFilePath = Path.Combine(environmentDirectory, managedFileName);
}
public string? Get(string name)
{
var fromFiles = LoadMergedFromDirectory();
if (fromFiles.TryGetValue(name, out var fileValue))
{
return fileValue;
}
return System.Environment.GetEnvironmentVariable(name);
}
public string? GetUserPersistedValue(string name)
{
if (LoadMergedFromDirectory().TryGetValue(name, out var value))
{
return value;
}
return null;
}
public void Set(string name, string value)
{
try
{
Directory.CreateDirectory(_environmentDirectory);
var managed = LoadManagedFile();
managed[name] = value;
WriteManagedFileAtomic(managed);
}
catch (Exception ex) when (ex is UnauthorizedAccessException or IOException)
{
throw new EnvironmentVariableStoreException(
$"Failed to write environment variable '{name}' to '{_managedFilePath}'.",
ex);
}
}
public bool Exists(string name) => Get(name) is not null;
public IReadOnlyDictionary<string, string> GetAll(IEnumerable<string> names)
{
var merged = LoadMergedFromDirectory();
var result = new Dictionary<string, string>(StringComparer.Ordinal);
foreach (var name in names)
{
if (merged.TryGetValue(name, out var value))
{
result[name] = value;
}
else
{
var processValue = System.Environment.GetEnvironmentVariable(name);
if (processValue is not null)
{
result[name] = processValue;
}
}
}
return result;
}
public IReadOnlyDictionary<string, string> GetProcessEnvironment() =>
ToDictionary(System.Environment.GetEnvironmentVariables());
public IReadOnlyDictionary<string, string> GetUserPersistedEnvironment() =>
LoadMergedFromDirectory();
public bool IsPersistedInUserStore(string name) =>
LoadMergedFromDirectory().ContainsKey(name);
public void RemoveFromUserStore(string name)
{
try
{
if (!Directory.Exists(_environmentDirectory))
{
return;
}
foreach (var file in Directory.EnumerateFiles(_environmentDirectory, "*.conf"))
{
var variables = SystemdEnvironmentFileParser.Parse(File.ReadAllText(file));
if (!variables.Remove(name))
{
continue;
}
WriteConfFileAtomic(file, variables);
}
}
catch (Exception ex) when (ex is UnauthorizedAccessException or IOException)
{
throw new EnvironmentVariableStoreException(
$"Failed to remove environment variable '{name}' from '{_environmentDirectory}'.",
ex);
}
}
private Dictionary<string, string> LoadManagedFile()
{
if (!File.Exists(_managedFilePath))
{
return new Dictionary<string, string>(StringComparer.Ordinal);
}
return SystemdEnvironmentFileParser.Parse(File.ReadAllText(_managedFilePath));
}
private Dictionary<string, string> LoadMergedFromDirectory()
{
var merged = new Dictionary<string, string>(StringComparer.Ordinal);
if (!Directory.Exists(_environmentDirectory))
{
return merged;
}
foreach (var file in Directory.EnumerateFiles(_environmentDirectory, "*.conf").Order(StringComparer.Ordinal))
{
var parsed = SystemdEnvironmentFileParser.Parse(File.ReadAllText(file));
foreach (var pair in parsed)
{
merged[pair.Key] = pair.Value;
}
}
return merged;
}
private void WriteManagedFileAtomic(Dictionary<string, string> variables) =>
WriteConfFileAtomic(_managedFilePath, variables);
private static void WriteConfFileAtomic(string path, Dictionary<string, string> variables)
{
var content = SystemdEnvironmentFileParser.Serialize(variables);
var tempPath = path + ".tmp";
File.WriteAllText(tempPath, content);
File.Move(tempPath, path, overwrite: true);
}
private static Dictionary<string, string> ToDictionary(System.Collections.IDictionary source)
{
var result = new Dictionary<string, string>(StringComparer.Ordinal);
foreach (string key in source.Keys)
{
result[key] = source[key]?.ToString() ?? string.Empty;
}
return result;
}
}