Загрузка данных


using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
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())
                {
                    UserInfo? userInfo = GetRealUserInfo();

                    if (userInfo == null)
                    {
                        throw new InvalidOperationException(
                            "Приложение запущено от root, но не удалось определить обычного пользователя"
                        );
                    }

                    StartKWriteAsUser(userInfo, adminFilePath);
                }
                else
                {
                    StartKWrite(adminFilePath);
                }
            }
            catch (Exception ex)
            {
                Locator.Instance.Resolve<ILogger>()
                    .Error($"Ошибка при открытии системного файла в KWrite: {ex.Message}");
            }
        });
    }

    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(UserInfo userInfo, string fileName)
    {
        var psi = new ProcessStartInfo
        {
            FileName = "runuser",
            UseShellExecute = false,
            CreateNoWindow = true
        };

        psi.ArgumentList.Add("-u");
        psi.ArgumentList.Add(userInfo.UserName);
        psi.ArgumentList.Add("--");

        psi.ArgumentList.Add("env");

        AddCurrentEnvironmentArgument(psi, "DISPLAY");
        AddCurrentEnvironmentArgument(psi, "WAYLAND_DISPLAY");
        AddCurrentEnvironmentArgument(psi, "QT_QPA_PLATFORM");
        AddCurrentEnvironmentArgument(psi, "XDG_CURRENT_DESKTOP");
        AddCurrentEnvironmentArgument(psi, "KDE_FULL_SESSION");

        psi.ArgumentList.Add($"HOME={userInfo.HomeDirectory}");
        psi.ArgumentList.Add($"USER={userInfo.UserName}");
        psi.ArgumentList.Add($"LOGNAME={userInfo.UserName}");
        psi.ArgumentList.Add($"XDG_RUNTIME_DIR=/run/user/{userInfo.UserId}");
        psi.ArgumentList.Add($"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/{userInfo.UserId}/bus");

        string xAuthority = Environment.GetEnvironmentVariable("XAUTHORITY")
                            ?? Path.Combine(userInfo.HomeDirectory, ".Xauthority");

        if (File.Exists(xAuthority))
            psi.ArgumentList.Add($"XAUTHORITY={xAuthority}");

        psi.ArgumentList.Add("kwrite");
        psi.ArgumentList.Add(fileName);

        Process.Start(psi);
    }

    private static void AddCurrentEnvironmentArgument(ProcessStartInfo psi, string name)
    {
        string? value = Environment.GetEnvironmentVariable(name);

        if (!string.IsNullOrWhiteSpace(value))
            psi.ArgumentList.Add($"{name}={value}");
    }

    private static UserInfo? GetRealUserInfo()
    {
        string? sudoUser = Environment.GetEnvironmentVariable("SUDO_USER");
        string? sudoUid = Environment.GetEnvironmentVariable("SUDO_UID");

        if (!string.IsNullOrWhiteSpace(sudoUser) &&
            !string.IsNullOrWhiteSpace(sudoUid) &&
            sudoUser != "root")
        {
            string homeDirectory = GetHomeDirectoryByUserName(sudoUser)
                                   ?? $"/home/{sudoUser}";

            return new UserInfo
            {
                UserName = sudoUser,
                UserId = sudoUid,
                HomeDirectory = homeDirectory
            };
        }

        string? pkexecUid = Environment.GetEnvironmentVariable("PKEXEC_UID");

        if (!string.IsNullOrWhiteSpace(pkexecUid))
        {
            var userInfo = GetUserInfoByUid(pkexecUid);

            if (userInfo != null)
                return userInfo;
        }

        return null;
    }

    private static string? GetHomeDirectoryByUserName(string userName)
    {
        try
        {
            if (!File.Exists("/etc/passwd"))
                return null;

            string? line = File.ReadLines("/etc/passwd")
                .FirstOrDefault(x => x.StartsWith(userName + ":", StringComparison.Ordinal));

            if (line == null)
                return null;

            string[] parts = line.Split(':');

            if (parts.Length < 6)
                return null;

            return parts[5];
        }
        catch
        {
            return null;
        }
    }

    private static UserInfo? GetUserInfoByUid(string uid)
    {
        try
        {
            if (!File.Exists("/etc/passwd"))
                return null;

            foreach (string line in File.ReadLines("/etc/passwd"))
            {
                string[] parts = line.Split(':');

                if (parts.Length < 6)
                    continue;

                string userName = parts[0];
                string userId = parts[2];
                string homeDirectory = parts[5];

                if (userId == uid && userName != "root")
                {
                    return new UserInfo
                    {
                        UserName = userName,
                        UserId = userId,
                        HomeDirectory = homeDirectory
                    };
                }
            }

            return null;
        }
        catch
        {
            return null;
        }
    }

    private sealed class UserInfo
    {
        public string UserName { get; set; } = string.Empty;

        public string UserId { get; set; } = string.Empty;

        public string HomeDirectory { get; set; } = string.Empty;
    }
}