mirror of
https://github.com/2dust/v2rayN.git
synced 2025-05-14 04:08:07 +00:00
Refactor Linux to run Tun as sudo
This commit is contained in:
parent
0032a3d27a
commit
adf3b955d6
6 changed files with 171 additions and 124 deletions
|
@ -31,15 +31,6 @@ public sealed class AppHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAdministrator
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
_isAdministrator ??= Utils.IsAdministrator();
|
|
||||||
return _isAdministrator.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LinuxSudoPwd { get; set; }
|
public string LinuxSudoPwd { get; set; }
|
||||||
|
|
||||||
#endregion Property
|
#endregion Property
|
||||||
|
|
144
v2rayN/ServiceLib/Handler/CoreAdminHandler.cs
Normal file
144
v2rayN/ServiceLib/Handler/CoreAdminHandler.cs
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
using CliWrap;
|
||||||
|
|
||||||
|
namespace ServiceLib.Handler;
|
||||||
|
|
||||||
|
public class CoreAdminHandler
|
||||||
|
{
|
||||||
|
private static readonly Lazy<CoreAdminHandler> _instance = new(() => new());
|
||||||
|
public static CoreAdminHandler Instance => _instance.Value;
|
||||||
|
private Config _config;
|
||||||
|
private Action<bool, string>? _updateFunc;
|
||||||
|
private const string _tag = "CoreAdminHandler";
|
||||||
|
private int _linuxSudoPid = -1;
|
||||||
|
|
||||||
|
public async Task Init(Config config, Action<bool, string> updateFunc)
|
||||||
|
{
|
||||||
|
if (_config != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_config = config;
|
||||||
|
_updateFunc = updateFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFunc(bool notify, string msg)
|
||||||
|
{
|
||||||
|
_updateFunc?.Invoke(notify, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Process?> 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<string> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ public class CoreHandler
|
||||||
private Config _config;
|
private Config _config;
|
||||||
private Process? _process;
|
private Process? _process;
|
||||||
private Process? _processPre;
|
private Process? _processPre;
|
||||||
private int _linuxSudoPid = -1;
|
private bool _linuxSudo = false;
|
||||||
private Action<bool, string>? _updateFunc;
|
private Action<bool, string>? _updateFunc;
|
||||||
private const string _tag = "CoreHandler";
|
private const string _tag = "CoreHandler";
|
||||||
|
|
||||||
|
@ -155,21 +155,21 @@ public class CoreHandler
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_linuxSudoPid > 0)
|
if (_linuxSudo)
|
||||||
{
|
{
|
||||||
await KillProcessAsLinuxSudo();
|
await CoreAdminHandler.Instance.KillProcessAsLinuxSudo();
|
||||||
_linuxSudoPid = -1;
|
_linuxSudo = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_process != null)
|
if (_process != null)
|
||||||
{
|
{
|
||||||
await ProcUtils.ProcessKill(_process, true);
|
await ProcUtils.ProcessKill(_process, Utils.IsWindows());
|
||||||
_process = null;
|
_process = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_processPre != null)
|
if (_processPre != null)
|
||||||
{
|
{
|
||||||
await ProcUtils.ProcessKill(_processPre, true);
|
await ProcUtils.ProcessKill(_processPre, Utils.IsWindows());
|
||||||
_processPre = null;
|
_processPre = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,13 +225,6 @@ public class CoreHandler
|
||||||
_updateFunc?.Invoke(notify, msg);
|
_updateFunc?.Invoke(notify, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsNeedSudo(ECoreType eCoreType)
|
|
||||||
{
|
|
||||||
return _config.TunModeItem.EnableTun
|
|
||||||
&& eCoreType == ECoreType.sing_box
|
|
||||||
&& Utils.IsNonWindows();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Private
|
#endregion Private
|
||||||
|
|
||||||
#region Process
|
#region Process
|
||||||
|
@ -245,6 +238,16 @@ public class CoreHandler
|
||||||
return null;
|
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
|
try
|
||||||
{
|
{
|
||||||
Process proc = new()
|
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)
|
if (displayLog)
|
||||||
{
|
{
|
||||||
proc.OutputDataReceived += (sender, e) =>
|
proc.OutputDataReceived += (sender, e) =>
|
||||||
{
|
{
|
||||||
if (e.Data.IsNullOrEmpty())
|
if (e.Data.IsNotEmpty())
|
||||||
return;
|
{
|
||||||
UpdateFunc(false, e.Data + Environment.NewLine);
|
UpdateFunc(false, e.Data + Environment.NewLine);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
proc.ErrorDataReceived += (sender, e) =>
|
proc.ErrorDataReceived += (sender, e) =>
|
||||||
{
|
{
|
||||||
if (e.Data.IsNullOrEmpty())
|
if (e.Data.IsNotEmpty())
|
||||||
return;
|
{
|
||||||
UpdateFunc(false, e.Data + Environment.NewLine);
|
UpdateFunc(false, e.Data + Environment.NewLine);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
proc.Start();
|
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)
|
if (displayLog)
|
||||||
{
|
{
|
||||||
proc.BeginOutputReadLine();
|
proc.BeginOutputReadLine();
|
||||||
|
@ -318,82 +308,4 @@ public class CoreHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Process
|
#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<string> 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
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -454,7 +454,7 @@ public class StatusBarViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
if (Utils.IsWindows())
|
if (Utils.IsWindows())
|
||||||
{
|
{
|
||||||
return AppHandler.Instance.IsAdministrator;
|
return Utils.IsAdministrator();
|
||||||
}
|
}
|
||||||
else if (Utils.IsLinux())
|
else if (Utils.IsLinux())
|
||||||
{
|
{
|
||||||
|
|
|
@ -143,7 +143,7 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (AppHandler.Instance.IsAdministrator)
|
if (Utils.IsAdministrator())
|
||||||
{
|
{
|
||||||
this.Title = $"{Utils.GetVersion()} - {ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp}";
|
this.Title = $"{Utils.GetVersion()} - {ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp}";
|
||||||
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp);
|
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp);
|
||||||
|
|
|
@ -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)
|
if (!_config.GuiItem.EnableHWA)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue