diff --git a/v2rayN/ServiceLib/Handler/AppHandler.cs b/v2rayN/ServiceLib/Handler/AppHandler.cs index 3f9fdf74..906e2ba3 100644 --- a/v2rayN/ServiceLib/Handler/AppHandler.cs +++ b/v2rayN/ServiceLib/Handler/AppHandler.cs @@ -31,15 +31,6 @@ public sealed class AppHandler } } - public bool IsAdministrator - { - get - { - _isAdministrator ??= Utils.IsAdministrator(); - return _isAdministrator.Value; - } - } - public string LinuxSudoPwd { get; set; } #endregion Property diff --git a/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs b/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs new file mode 100644 index 00000000..85d90fc3 --- /dev/null +++ b/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs @@ -0,0 +1,144 @@ +using System.Diagnostics; +using System.Text; +using CliWrap; + +namespace ServiceLib.Handler; + +public class CoreAdminHandler +{ + private static readonly Lazy _instance = new(() => new()); + public static CoreAdminHandler Instance => _instance.Value; + private Config _config; + private Action? _updateFunc; + private const string _tag = "CoreAdminHandler"; + private int _linuxSudoPid = -1; + + public async Task Init(Config config, Action updateFunc) + { + if (_config != null) + { + return; + } + _config = config; + _updateFunc = updateFunc; + } + + private void UpdateFunc(bool notify, string msg) + { + _updateFunc?.Invoke(notify, msg); + } + + public async Task RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath) + { + try + { + var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}"; + var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh"); + + Process proc = new() + { + StartInfo = new() + { + FileName = shFilePath, + Arguments = "", + WorkingDirectory = Utils.GetBinConfigPath(), + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + StandardInputEncoding = Encoding.UTF8, + StandardOutputEncoding = Encoding.UTF8, + StandardErrorEncoding = Encoding.UTF8, + } + }; + + proc.OutputDataReceived += (sender, e) => + { + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } + }; + proc.ErrorDataReceived += (sender, e) => + { + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } + }; + + proc.Start(); + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); + + await Task.Delay(10); + await proc.StandardInput.WriteLineAsync(); + await Task.Delay(10); + await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd); + + await Task.Delay(100); + if (proc is null or { HasExited: true }) + { + throw new Exception(ResUI.FailedToRunCore); + } + + _linuxSudoPid = proc.Id; + + return proc; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + UpdateFunc(false, ex.Message); + return null; + } + } + + public async Task KillProcessAsLinuxSudo() + { + if (_linuxSudoPid < 0) + { + return; + } + + try + { + var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}"; + var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh"); + + var result = await Cli.Wrap(shFilePath) + .WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd)) + .ExecuteAsync(); + + _linuxSudoPid = -1; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + UpdateFunc(false, ex.Message); + } + } + + private async Task CreateLinuxShellFile(string cmdLine, string fileName) + { + var shFilePath = Utils.GetBinConfigPath(fileName); + File.Delete(shFilePath); + + var sb = new StringBuilder(); + sb.AppendLine("#!/bin/sh"); + if (Utils.IsAdministrator()) + { + sb.AppendLine($"{cmdLine}"); + } + else + { + sb.AppendLine($"sudo -S {cmdLine}"); + } + + await File.WriteAllTextAsync(shFilePath, sb.ToString()); + await Utils.SetLinuxChmod(shFilePath); + + return shFilePath; + } +} diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index a2f24df8..97918693 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -13,7 +13,7 @@ public class CoreHandler private Config _config; private Process? _process; private Process? _processPre; - private int _linuxSudoPid = -1; + private bool _linuxSudo = false; private Action? _updateFunc; private const string _tag = "CoreHandler"; @@ -155,21 +155,21 @@ public class CoreHandler { try { - if (_linuxSudoPid > 0) + if (_linuxSudo) { - await KillProcessAsLinuxSudo(); - _linuxSudoPid = -1; + await CoreAdminHandler.Instance.KillProcessAsLinuxSudo(); + _linuxSudo = false; } if (_process != null) { - await ProcUtils.ProcessKill(_process, true); + await ProcUtils.ProcessKill(_process, Utils.IsWindows()); _process = null; } if (_processPre != null) { - await ProcUtils.ProcessKill(_processPre, true); + await ProcUtils.ProcessKill(_processPre, Utils.IsWindows()); _processPre = null; } } @@ -225,13 +225,6 @@ public class CoreHandler _updateFunc?.Invoke(notify, msg); } - private bool IsNeedSudo(ECoreType eCoreType) - { - return _config.TunModeItem.EnableTun - && eCoreType == ECoreType.sing_box - && Utils.IsNonWindows(); - } - #endregion Private #region Process @@ -245,6 +238,16 @@ public class CoreHandler return null; } + if (mayNeedSudo + && _config.TunModeItem.EnableTun + && coreInfo.CoreType == ECoreType.sing_box + && Utils.IsNonWindows()) + { + _linuxSudo = true; + await CoreAdminHandler.Instance.Init(_config, _updateFunc); + return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath); + } + try { Process proc = new() @@ -263,38 +266,25 @@ public class CoreHandler } }; - var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType); - if (isNeedSudo) - { - await RunProcessAsLinuxSudo(proc, fileName, coreInfo, configPath); - } - if (displayLog) { proc.OutputDataReceived += (sender, e) => { - if (e.Data.IsNullOrEmpty()) - return; - UpdateFunc(false, e.Data + Environment.NewLine); + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } }; proc.ErrorDataReceived += (sender, e) => { - if (e.Data.IsNullOrEmpty()) - return; - UpdateFunc(false, e.Data + Environment.NewLine); + if (e.Data.IsNotEmpty()) + { + UpdateFunc(false, e.Data + Environment.NewLine); + } }; } proc.Start(); - if (isNeedSudo && AppHandler.Instance.LinuxSudoPwd.IsNotEmpty()) - { - await proc.StandardInput.WriteLineAsync(); - await Task.Delay(10); - await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd); - } - if (isNeedSudo) - _linuxSudoPid = proc.Id; - if (displayLog) { proc.BeginOutputReadLine(); @@ -318,82 +308,4 @@ public class CoreHandler } #endregion Process - - #region Linux - - private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath) - { - var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}"; - - var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh"); - proc.StartInfo.FileName = shFilePath; - proc.StartInfo.Arguments = ""; - proc.StartInfo.WorkingDirectory = ""; - if (AppHandler.Instance.LinuxSudoPwd.IsNotEmpty()) - { - proc.StartInfo.StandardInputEncoding = Encoding.UTF8; - proc.StartInfo.RedirectStandardInput = true; - } - } - - private async Task KillProcessAsLinuxSudo() - { - var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}"; - var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh"); - Process proc = new() - { - StartInfo = new() - { - FileName = shFilePath, - UseShellExecute = false, - CreateNoWindow = true, - StandardInputEncoding = Encoding.UTF8, - RedirectStandardInput = true - } - }; - proc.Start(); - - if (AppHandler.Instance.LinuxSudoPwd.IsNotEmpty()) - { - try - { - await proc.StandardInput.WriteLineAsync(); - await Task.Delay(10); - await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd); - } - catch (Exception) - { - // ignored - } - } - - var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)); - await proc.WaitForExitAsync(timeout.Token); - await Task.Delay(1000); - } - - private async Task CreateLinuxShellFile(string cmdLine, string fileName) - { - //Shell scripts - var shFilePath = Utils.GetBinConfigPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName); - File.Delete(shFilePath); - var sb = new StringBuilder(); - sb.AppendLine("#!/bin/sh"); - if (AppHandler.Instance.IsAdministrator) - { - sb.AppendLine($"{cmdLine}"); - } - else - { - sb.AppendLine($"sudo -S {cmdLine}"); - } - - await File.WriteAllTextAsync(shFilePath, sb.ToString()); - await Utils.SetLinuxChmod(shFilePath); - //Logging.SaveLog(shFilePath); - - return shFilePath; - } - - #endregion Linux } diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index ec6ec39e..2c48d0bf 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -454,7 +454,7 @@ public class StatusBarViewModel : MyReactiveObject { if (Utils.IsWindows()) { - return AppHandler.Instance.IsAdministrator; + return Utils.IsAdministrator(); } else if (Utils.IsLinux()) { diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index 91328046..34852a50 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -143,7 +143,7 @@ public partial class MainWindow : ReactiveWindow } else { - if (AppHandler.Instance.IsAdministrator) + if (Utils.IsAdministrator()) { this.Title = $"{Utils.GetVersion()} - {ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp}"; NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp); diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs index b837f65c..b3869cae 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs @@ -132,7 +132,7 @@ public partial class MainWindow } }); - this.Title = $"{Utils.GetVersion()} - {(AppHandler.Instance.IsAdministrator ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}"; + this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}"; if (!_config.GuiItem.EnableHWA) {