178 lines
5.4 KiB
C#
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;
|
|
}
|
|
}
|