Загрузка данных
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
public static class SystemFileEditor
{
[DllImport("libc")]
private static extern uint geteuid();
public static void OpenSystemFileInKWrite(string fileName)
{
Task.Run(() =>
{
try
{
if (string.IsNullOrWhiteSpace(fileName))
throw new ArgumentException("Не указан путь к файлу");
string adminFilePath = ToKdeAdminPath(fileName);
if (IsRoot())
{
string? userName = GetRealUserName();
if (string.IsNullOrWhiteSpace(userName))
throw new InvalidOperationException(
"Приложение запущено от root, но не удалось определить обычного пользователя для запуска KWrite"
);
StartKWriteAsUser(userName, adminFilePath);
}
else
{
StartKWrite(adminFilePath);
}
}
catch (Exception ex)
{
Locator.Instance.Resolve<ILogger>()
.Error($"Ошибка при открытии системного файла в KWrite: {ex.Message}");
}
}).ConfigureAwait(false);
}
private static bool IsRoot()
{
return OperatingSystem.IsLinux() && geteuid() == 0;
}
private static string ToKdeAdminPath(string fileName)
{
string fullPath = Path.GetFullPath(fileName);
// /etc/hosts -> admin:///etc/hosts
return "admin://" + fullPath;
}
private static void StartKWrite(string fileName)
{
var psi = new ProcessStartInfo
{
FileName = "kwrite",
UseShellExecute = false,
CreateNoWindow = true
};
psi.ArgumentList.Add(fileName);
Process.Start(psi);
}
private static void StartKWriteAsUser(string userName, string fileName)
{
string userId = GetUserId(userName);
var psi = new ProcessStartInfo
{
FileName = "runuser",
UseShellExecute = false,
CreateNoWindow = true
};
psi.ArgumentList.Add("-u");
psi.ArgumentList.Add(userName);
psi.ArgumentList.Add("--");
psi.ArgumentList.Add("env");
AddEnvironmentArgument(psi, "DISPLAY");
AddEnvironmentArgument(psi, "WAYLAND_DISPLAY");
AddEnvironmentArgument(psi, "XAUTHORITY");
AddEnvironmentArgument(psi, "QT_QPA_PLATFORM");
AddEnvironmentArgument(psi, "XDG_CURRENT_DESKTOP");
AddEnvironmentArgument(psi, "KDE_FULL_SESSION");
psi.ArgumentList.Add($"XDG_RUNTIME_DIR=/run/user/{userId}");
psi.ArgumentList.Add($"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/{userId}/bus");
psi.ArgumentList.Add("kwrite");
psi.ArgumentList.Add(fileName);
Process.Start(psi);
}
private static void AddEnvironmentArgument(ProcessStartInfo psi, string name)
{
string? value = Environment.GetEnvironmentVariable(name);
if (!string.IsNullOrWhiteSpace(value))
psi.ArgumentList.Add($"{name}={value}");
}
private static string? GetRealUserName()
{
string? sudoUser = Environment.GetEnvironmentVariable("SUDO_USER");
if (!string.IsNullOrWhiteSpace(sudoUser) && sudoUser != "root")
return sudoUser;
string? pkexecUid = Environment.GetEnvironmentVariable("PKEXEC_UID");
if (!string.IsNullOrWhiteSpace(pkexecUid))
return RunCommandAndGetOutput("id", "-nu", pkexecUid);
return null;
}
private static string GetUserId(string userName)
{
string? result = RunCommandAndGetOutput("id", "-u", userName);
if (string.IsNullOrWhiteSpace(result))
throw new InvalidOperationException($"Не удалось определить UID пользователя {userName}");
return result;
}
private static string? RunCommandAndGetOutput(string fileName, params string[] arguments)
{
var psi = new ProcessStartInfo
{
FileName = fileName,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
foreach (string argument in arguments)
psi.ArgumentList.Add(argument);
using var process = Process.Start(psi);
if (process == null)
return null;
string output = process.StandardOutput.ReadToEnd().Trim();
process.WaitForExit();
return process.ExitCode == 0 ? output : null;
}
}