diff --git a/v2rayN/ServiceLib/Handler/CoreHandler.cs b/v2rayN/ServiceLib/Handler/CoreHandler.cs index 77d91597..94cad4a5 100644 --- a/v2rayN/ServiceLib/Handler/CoreHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreHandler.cs @@ -68,6 +68,7 @@ namespace ServiceLib.Handler { ShowMsg(true, $"{node.GetSummary()}"); await CoreStop(); + await Task.Delay(100); await CoreStart(node); //In tun mode, do a delay check and restart the core @@ -169,21 +170,12 @@ namespace ServiceLib.Handler ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}"); ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); - //ECoreType coreType; - //if (node.configType != EConfigType.Custom && _config.tunModeItem.enableTun) - //{ - // coreType = ECoreType.sing_box; - //} - //else - //{ - // coreType = LazyConfig.Instance.GetCoreType(node, node.configType); - //} var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType); _config.RunningCoreType = coreType; var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog; - var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog); + var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true); if (proc is null) { return; @@ -225,7 +217,7 @@ namespace ServiceLib.Handler if (result.Success) { var coreInfo2 = CoreInfoHandler.Instance.GetCoreInfo(preCoreType); - var proc2 = await RunProcess(coreInfo2, Global.CorePreConfigFileName, true); + var proc2 = await RunProcess(coreInfo2, Global.CorePreConfigFileName, true, true); if (proc2 is not null) { _processPre = proc2; @@ -243,7 +235,7 @@ namespace ServiceLib.Handler try { var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType); - var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true); + var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false); if (proc is null) { return -1; @@ -264,17 +256,28 @@ namespace ServiceLib.Handler _updateFunc?.Invoke(notify, msg); } + private bool IsNeedSudo(ECoreType eCoreType) + { + return _config.TunModeItem.EnableTun + && eCoreType == ECoreType.sing_box + && Utils.IsLinux() + && _config.TunModeItem.LinuxSudoPassword.IsNotEmpty() + ; + } + #endregion Private #region Process - private async Task RunProcess(CoreInfo coreInfo, string configPath, bool displayLog) + private async Task RunProcess(CoreInfo coreInfo, string configPath, bool displayLog, bool mayNeedSudo) { var fileName = CoreFindExe(coreInfo); if (Utils.IsNullOrEmpty(fileName)) { return null; } + + var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType); try { Process proc = new() @@ -292,6 +295,15 @@ namespace ServiceLib.Handler StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, } }; + + if (isNeedSudo) + { + proc.StartInfo.FileName = $"/bin/sudo"; + proc.StartInfo.Arguments = $"-S {fileName} {string.Format(coreInfo.Arguments, configPath)}"; + proc.StartInfo.StandardInputEncoding = Encoding.UTF8; + proc.StartInfo.RedirectStandardInput = true; + } + var startUpErrorMessage = new StringBuilder(); var startUpSuccessful = false; if (displayLog) @@ -313,6 +325,15 @@ namespace ServiceLib.Handler }; } proc.Start(); + + if (isNeedSudo) + { + await Task.Delay(10); + await proc.StandardInput.WriteLineAsync(_config.TunModeItem.LinuxSudoPassword); + await Task.Delay(10); + await proc.StandardInput.WriteLineAsync(_config.TunModeItem.LinuxSudoPassword); + } + if (displayLog) { proc.BeginOutputReadLine(); diff --git a/v2rayN/ServiceLib/Models/ConfigItems.cs b/v2rayN/ServiceLib/Models/ConfigItems.cs index 84c9ff3f..8ec47b88 100644 --- a/v2rayN/ServiceLib/Models/ConfigItems.cs +++ b/v2rayN/ServiceLib/Models/ConfigItems.cs @@ -161,6 +161,7 @@ public int Mtu { get; set; } public bool EnableExInbound { get; set; } public bool EnableIPv6Address { get; set; } + public string? LinuxSudoPassword { get; set; } } [Serializable] diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 26864bef..ccb2b939 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -3148,6 +3148,42 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Linux system sudo password 的本地化字符串。 + /// + public static string TbSettingsLinuxSudoPassword { + get { + return ResourceManager.GetString("TbSettingsLinuxSudoPassword", resourceCulture); + } + } + + /// + /// 查找类似 Please set the sudo password in Tun mode settings first 的本地化字符串。 + /// + public static string TbSettingsLinuxSudoPasswordIsEmpty { + get { + return ResourceManager.GetString("TbSettingsLinuxSudoPasswordIsEmpty", resourceCulture); + } + } + + /// + /// 查找类似 Please do not run this app with sudo 的本地化字符串。 + /// + public static string TbSettingsLinuxSudoPasswordNotSudoRunApp { + get { + return ResourceManager.GetString("TbSettingsLinuxSudoPasswordNotSudoRunApp", resourceCulture); + } + } + + /// + /// 查找类似 The password will only be stored in the local file. 的本地化字符串。 + /// + public static string TbSettingsLinuxSudoPasswordTip { + get { + return ResourceManager.GetString("TbSettingsLinuxSudoPasswordTip", resourceCulture); + } + } + /// /// 查找类似 Enable Log 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 41753419..099887ef 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1369,4 +1369,16 @@ (Domain or IP or ProcName) and Port and Protocol and InboundTag => OutboundTag + + Linux system sudo password + + + The password will only be stored in the local file. + + + Please set the sudo password in Tun mode settings first + + + Please do not run this app with sudo + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 0ef7eaef..c5238179 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1369,4 +1369,16 @@ Remarks Memo + + Linux system sudo password + + + The password will only be stored in the local file. + + + Please set the sudo password in Tun mode settings first + + + Please do not run this app with sudo + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index bd2d6cd6..255b578e 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1369,4 +1369,16 @@ Remote (WebDAV) + + Linux system sudo password + + + The password will only be stored in the local file. + + + Please set the sudo password in Tun mode settings first + + + Please do not run this app with sudo + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index 7494aa4d..07f9b463 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1366,4 +1366,16 @@ 备注备忘 + + Linux系统的sudo密码 + + + 密码只会存储在本地文件中,没有密码无法开启Tun + + + 请先在Tun模式设置中设置sudo密码 + + + 请不要用sudo运行本app + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 597a5b76..1a172b35 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1366,4 +1366,16 @@ 混淆密碼(obfs password) + + Linux系統的sudo密碼 + + + 密碼只會儲存在本機檔案中,沒有密碼無法開啟Tun + + + 請先在Tun模式設定中設定sudo密碼 + + + 請不要用sudo來運行本app + \ No newline at end of file diff --git a/v2rayN/ServiceLib/ServiceLib.csproj b/v2rayN/ServiceLib/ServiceLib.csproj index 0511465a..18aeed52 100644 --- a/v2rayN/ServiceLib/ServiceLib.csproj +++ b/v2rayN/ServiceLib/ServiceLib.csproj @@ -14,7 +14,7 @@ - + diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index 65369944..40aadaca 100644 --- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -85,6 +85,7 @@ namespace ServiceLib.ViewModels [Reactive] public int TunMtu { get; set; } [Reactive] public bool TunEnableExInbound { get; set; } [Reactive] public bool TunEnableIPv6Address { get; set; } + [Reactive] public string TunLinuxSudoPassword { get; set; } #endregion Tun mode @@ -197,6 +198,7 @@ namespace ServiceLib.ViewModels TunMtu = _config.TunModeItem.Mtu; TunEnableExInbound = _config.TunModeItem.EnableExInbound; TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address; + TunLinuxSudoPassword = _config.TunModeItem.LinuxSudoPassword; #endregion Tun mode @@ -340,6 +342,7 @@ namespace ServiceLib.ViewModels _config.TunModeItem.Mtu = TunMtu; _config.TunModeItem.EnableExInbound = TunEnableExInbound; _config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address; + _config.TunModeItem.LinuxSudoPassword = TunLinuxSudoPassword; //coreType await SaveCoreType(); diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index 70de9685..47c7312a 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -108,7 +108,7 @@ namespace ServiceLib.ViewModels SelectedServer = new(); RunningServerToolTipText = "-"; - if (_config.TunModeItem.EnableTun && AppHandler.Instance.IsAdministrator) + if (_config.TunModeItem.EnableTun && AllowEnableTun()) { EnableTun = true; } @@ -414,10 +414,17 @@ namespace ServiceLib.ViewModels { _config.TunModeItem.EnableTun = EnableTun; // When running as a non-administrator, reboot to administrator mode - if (EnableTun && !AppHandler.Instance.IsAdministrator) + if (EnableTun && AllowEnableTun() == false) { _config.TunModeItem.EnableTun = false; - Locator.Current.GetService()?.RebootAsAdmin(); + if (Utils.IsWindows()) + { + Locator.Current.GetService()?.RebootAsAdmin(); + } + else if (Utils.IsLinux()) + { + NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordIsEmpty); + } return; } await ConfigHandler.SaveConfig(_config); @@ -425,6 +432,19 @@ namespace ServiceLib.ViewModels } } + private bool AllowEnableTun() + { + if (Utils.IsWindows()) + { + return AppHandler.Instance.IsAdministrator; + } + else if (Utils.IsLinux()) + { + return _config.TunModeItem.LinuxSudoPassword.IsNotEmpty(); + } + return false; + } + #endregion System proxy and Routings #region UI diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index 4d4e67f7..377e4689 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -117,6 +117,11 @@ namespace v2rayN.Desktop.Views } else { + if (AppHandler.Instance.IsAdministrator) + { + this.Title = $"{Utils.GetVersion()} - {ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp}"; + NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp); + } menuRebootAsAdmin.IsVisible = false; menuSettingsSetUWP.IsVisible = false; menuGlobalHotkeySetting.IsVisible = false; @@ -282,6 +287,7 @@ namespace v2rayN.Desktop.Views e.Cancel = true; ShowHideWindow(false); break; + case WindowCloseReason.ApplicationShutdown or WindowCloseReason.OSShutdown: await ViewModel?.MyAppExitAsync(true); break; diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index e56e6c56..6176dfe3 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -489,7 +489,7 @@ HorizontalAlignment="Left" Classes="Margin8" /> - + + @@ -794,6 +795,28 @@ Grid.Column="1" HorizontalAlignment="Left" Classes="Margin8" /> + + + + diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs index 2a024fd7..22b96ce8 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs @@ -152,6 +152,7 @@ namespace v2rayN.Desktop.Views this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunEnableExInbound, v => v.togEnableExInbound.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.TunLinuxSudoPassword, v => v.txtLinuxSudoPassword.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables); diff --git a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs index db574dc3..63587ef9 100644 --- a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs @@ -41,7 +41,7 @@ namespace v2rayN.Desktop.Views this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables); }); - spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator); + //spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator); } private async Task UpdateViewHandler(EViewAction action, object? obj)