Skip to content

再次尝试移植到nixos #4091

Description

@Aozora-Wings

我做了一些尝试:包括:检测nixos的环境,检测系统配置下的特权文件.
然后卡在了这里:
Plugins.cs

    private static bool IsNixOS()
    {
        if (_isNixOS.HasValue)
            return _isNixOS.Value;

        try
        {
            if (File.Exists("/etc/os-release"))
            {
                var osRelease = File.ReadAllText("/etc/os-release");
                if (osRelease.Contains("ID=nixos", StringComparison.OrdinalIgnoreCase))
                {
                    _isNixOS = true;
                    return true;
                }
            }

            var nixStoreDir = new DirectoryInfo("/nix/store");
            if (nixStoreDir.Exists && nixStoreDir.GetDirectories().Any(d => d.Name.Length > 30 && d.Name.Contains("-")))
            {
                _isNixOS = true;
                return true;
            }
        }
        catch { }

        _isNixOS = false;
        return false;
    }

    internal string? SubProcessPath
    {
        get
        {
            if (subProcessPath == null)
            {
                try
                {
                    subProcessPath = Assembly.GetExecutingAssembly().Location;
                    subProcessPath = Path.GetDirectoryName(subProcessPath);
                    subProcessPath.ThrowIsNull();

                    const string fileName = $"Steam++.{moduleName}";
                    var subProcessFileName = OperatingSystem.IsWindows() ? $"{fileName}{FileEx.EXE}" : fileName;
                    
                    // NixOS 环境下优先使用 security.wrappers
                    if (OperatingSystem.IsLinux() && IsNixOS())
                    {
                        var wrapperName = "steampp-accelerator";
                        var wrapperPath = Path.Combine("/run/wrappers/bin", wrapperName);
                        
                        Log.Info(nameof(Plugin), "NixOS detected, checking wrapper: {path}", wrapperPath);
                        Log.Info(nameof(Plugin), "Wrapper exists: {exists}", File.Exists(wrapperPath));
                        
                        if (File.Exists(wrapperPath))
                        {
                            Log.Info(nameof(Plugin), "Using NixOS wrapper: {path}", wrapperPath);
                            subProcessPath = wrapperPath;
                            return subProcessPath;
                        }
                        else
                        {
                            Log.Warn(nameof(Plugin), "NixOS detected but wrapper not found ({path}), falling back to default path", wrapperPath);
                        }
                    }
                    
                    subProcessPath = Path.Combine(subProcessPath, subProcessFileName);
                    Log.Info(nameof(Plugin), "Using default subprocess path: {path}", subProcessPath);
                    Log.Info(nameof(Plugin), "Default subprocess exists: {exists}", File.Exists(subProcessPath));
                    
                    return subProcessPath;

    #if DEBUG
                    if (!File.Exists(subProcessPath))
                    {
                        subProcessPath = Path.Combine(ProjectUtils.ProjPath, "src", "BD.WTTS.Client.Plugins.Accelerator.ReverseProxy", "bin", "Debug", $"net{Environment.Version.Major}.{Environment.Version.Minor}", subProcessFileName);
                    }
    #endif
                    return subProcessPath;
                }
                catch (Exception ex)
                {
                    Log.Error(nameof(Plugin), ex, "Error getting SubProcessPath");
                    subProcessPath = string.Empty;
                }
            }

            return subProcessPath;
        }
    }

   public override async ValueTask OnInitializeAsync()
    {
        var ipc = IPCMainProcessService.Instance;

        // 启动加速模块子进程
        await ipc.AddDaemonWithStartSubProcessAsync(moduleName, async ipc =>
        {
            var subProcessPath = SubProcessPath;
            
            // 在 NixOS 上,如果我们检测到这是一个设置了 capabilities 的 wrapper,
            // 我们需要处理 .NET App Host 与 capabilities 的兼容性问题
            // bool isAdminRequired = !(OperatingSystem.IsLinux() && IsNixOS());
            bool isAdminRequired = false;

        Log.Info(nameof(Plugin), "Starting subprocess: {path}, admin required: {isAdminRequired}", 
            subProcessPath, isAdminRequired);

        // 尝试启动子进程
        Process? process = null;
        try
        {
            process = await ipc.StartSubProcessAsync(subProcessPath.ThrowIsNull(),
                isAdministrator: isAdminRequired);
        }
        catch (Exception ex)
        {
            Log.Error(nameof(Plugin), ex, "Failed to start subprocess, trying fallback method");
            
            // 如果直接启动失败,尝试其他方法
            if (OperatingSystem.IsLinux() && IsNixOS())
            {
                // 尝试使用不同的启动方式
                var psi = new ProcessStartInfo
                {
                    FileName = subProcessPath,
                    UseShellExecute = false,
                    CreateNoWindow = true,
                    Arguments = "" // 可能需要传递一些参数
                };
                
                // 添加必要的环境变量
                psi.Environment["DOTNET_ROOT"] = Environment.GetEnvironmentVariable("DOTNET_ROOT");
                
                try
                {
                    process = Process.Start(psi);
                }
                catch (Exception ex2)
                {
                    Log.Error(nameof(Plugin), ex2, "Fallback method also failed");
                    throw;
                }
            }
            else
            {
                throw;
            }
        }
        
        return process;
    });
}

当检测到文件后 就直接报错:failed to inherit capabilities: Operation not permitted.此时我已经在系统下配置了

      security.wrappers.steampp-accelerator = {
      owner = "root";
      group = "root";
      capabilities = "cap_net_bind_service,cap_net_raw,cap_net_admin=+ep";
      source = "${pkgs.SteamTools-my}/bin/modules/Accelerator/Steam++.Accelerator";
    };

这个文件也确实有:
/run/wrappers/bin/steampp-accelerator
然后
我在SteamTools/src/BD.WTTS.Client.Plugins.Accelerator/Services/Mvvm/ProxyService.Operate.cs
下添加了

if (startProxyResult)
        {
            switch (proxyMode)
            {
                case ProxyMode.Hosts: // 启动加速服务后修改 Hosts 文件
                    if (proxyDomains.Any_Nullable())
                    {
                        var localhost = IPAddress.Any.Equals(proxyIp_.Value) ?
                            IPAddress.Loopback.ToString() : proxyIp!;
                        var hosts = proxyDomains.SelectMany(s =>
                        {
                            if (s == null) return default!;

                            return s.ListeningDomainNamesArray.Select(host =>
                            {
                                if (host.Contains(' '))
                                {
                                    var h = host.Split(' ');
                                    return new KeyValuePair<string, string>(h[1], h[0]);
                                }
                                return new KeyValuePair<string, string>(host, localhost);
                            });
                        }).ToDictionaryIgnoreRepeat(x => x.Key, y => y.Value);
                        if (isEnableScript)
                        {
                            hosts.TryAdd(IReverseProxyService.Constants.LocalDomain, localhost);
                        }
                        var updateHostsResult = await hostsFileService.UpdateHosts(hosts);
                        if (updateHostsResult.ResultType != OperationResultType.Success)
                        {
                            if (OperatingSystem2.IsMacOS() || OperatingSystem2.IsLinux())
                            {
                                Browser2.Open(Constants.Urls.OfficialWebsite_UnixHostAccess);
                                //platformService.RunShell($" \\cp \"{Path.Combine(IOPath.CacheDirectory, "hosts")}\" \"{platformService.HostsFilePath}\"");
                            }
                            await reverseProxyService.StopProxyAsync();
                            return Strings.OperationHostsError_.Format(updateHostsResult.Message);
                        }
                    }
                    break;
            }
            _StartAccelerateTime = DateTimeOffset.Now;
            StartTimer();
            return default;
        }
        else
        {
            if (startProxyResult.Code == StartProxyResultCode.BindPortError)
            {
                // NixOS 下通过 security.wrappers 机制处理权限,不需要 setcap
                if (NixOSDetector.IsNixOS())
                {
                    // 在 NixOS 上,如果绑定失败,说明 wrapper 未正确配置
                    Toast.Show(ToastIcon.Error, "NixOS 环境下需要配置 security.wrappers 以启用特权端口绑定");
                    reverseProxyService.Exit();
                    return "StartProxyFail: BindPortError (NixOS wrapper not configured)";
                }

                if (!string.IsNullOrWhiteSpace(Plugin.Instance.SubProcessPath))
                {
                    // 非 NixOS 的 Linux 系统使用 pkexec setcap
                    Process.Start("pkexec", new string[] { "setcap", "cap_net_bind_service=+eip", Plugin.Instance.SubProcessPath }).WaitForExit();
                }
                //新线程等待 IPC 错误返回后 Kill 自己 Linux 修改权限需要重新启动
                reverseProxyService.Exit();
                return "StartProxyFail: BindPortError";
            }
            else
            {
                var errorString = startProxyResult.Code switch
                {
                    StartProxyResultCode.Exception => startProxyResult.Exception?.ToString() ?? nameof(StartProxyResultCode.Exception),
                    _ => $"StartProxyFail, ErrCode: {startProxyResult.Code}"
                };
                return errorString;
            }
        }

当文件不存在的时候能弹出消息。文件存在的时候直接报错:failed to inherit capabilities: Operation not permitted.
需要请教。

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions