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


using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

public static class KWriteLauncher
{
    [DllImport("libc")]
    private static extern uint geteuid();

    public static void OpenKWriteAsOriginalUser(string filePath)
    {
        if (string.IsNullOrWhiteSpace(filePath))
            throw new ArgumentException("Путь к файлу не указан.", nameof(filePath));

        string fullPath = Path.GetFullPath(filePath);

        if (!File.Exists(fullPath))
            throw new FileNotFoundException("Файл не найден.", fullPath);

        bool isRoot = geteuid() == 0;

        // Если приложение НЕ запущено через sudo/root — открываем обычно.
        if (!isRoot)
        {
            StartDetached("kwrite", fullPath);
            return;
        }

        string? sudoUser = Environment.GetEnvironmentVariable("SUDO_USER");
        string? sudoUid = Environment.GetEnvironmentVariable("SUDO_UID");

        if (string.IsNullOrWhiteSpace(sudoUser) || string.IsNullOrWhiteSpace(sudoUid))
        {
            throw new InvalidOperationException(
                "Приложение запущено от root, но SUDO_USER/SUDO_UID не найдены. " +
                "Нужно явно знать пользователя графической сессии."
            );
        }

        string home = GetHomeDirectory(sudoUser)
            ?? throw new InvalidOperationException($"Не удалось определить HOME для пользователя {sudoUser}.");

        string runtimeDir = $"/run/user/{sudoUid}";
        string dbusAddress = $"unix:path={runtimeDir}/bus";

        if (!Directory.Exists(runtimeDir))
        {
            throw new InvalidOperationException(
                $"Не найден {runtimeDir}. Пользователь {sudoUser} должен быть залогинен в графическую сессию."
            );
        }

        string display = Environment.GetEnvironmentVariable("DISPLAY") ?? ":0";
        string? waylandDisplay = Environment.GetEnvironmentVariable("WAYLAND_DISPLAY");
        string? xAuthority = Environment.GetEnvironmentVariable("XAUTHORITY");

        if (string.IsNullOrWhiteSpace(xAuthority))
        {
            string defaultXauthority = Path.Combine(home, ".Xauthority");
            if (File.Exists(defaultXauthority))
                xAuthority = defaultXauthority;
        }

        var psi = new ProcessStartInfo
        {
            FileName = "setsid",
            UseShellExecute = false,
            CreateNoWindow = true
        };

        // setsid -f — отделяет процесс, чтобы ваше приложение точно не зависело от KWrite.
        psi.ArgumentList.Add("-f");

        // runuser — запускает команду от имени обычного пользователя.
        psi.ArgumentList.Add("runuser");
        psi.ArgumentList.Add("-u");
        psi.ArgumentList.Add(sudoUser);
        psi.ArgumentList.Add("--");

        // env — задаем корректное окружение графической сессии пользователя.
        psi.ArgumentList.Add("env");
        psi.ArgumentList.Add($"HOME={home}");
        psi.ArgumentList.Add($"USER={sudoUser}");
        psi.ArgumentList.Add($"LOGNAME={sudoUser}");
        psi.ArgumentList.Add($"XDG_RUNTIME_DIR={runtimeDir}");
        psi.ArgumentList.Add($"DBUS_SESSION_BUS_ADDRESS={dbusAddress}");
        psi.ArgumentList.Add($"DISPLAY={display}");

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

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

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

        Process? process = Process.Start(psi);

        // Не вызываем WaitForExit().
        process?.Dispose();
    }

    private static void StartDetached(string command, string argument)
    {
        var psi = new ProcessStartInfo
        {
            FileName = "setsid",
            UseShellExecute = false,
            CreateNoWindow = true
        };

        psi.ArgumentList.Add("-f");
        psi.ArgumentList.Add(command);
        psi.ArgumentList.Add(argument);

        Process? process = Process.Start(psi);
        process?.Dispose();
    }

    private static string? GetHomeDirectory(string userName)
    {
        foreach (string line in File.ReadLines("/etc/passwd"))
        {
            if (line.StartsWith(userName + ":", StringComparison.Ordinal))
            {
                string[] parts = line.Split(':');
                if (parts.Length >= 6)
                    return parts[5];
            }
        }

        return null;
    }
}