From 3d23f3e3a27ec5a94bfc8faa6b7f0c109afe2a41 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 30 Jul 2025 19:52:45 +0800 Subject: [PATCH] Optimized and improved the code Optimized and improved the code for killing core processes in non-Windows environments. Now uses a shell script for precise processing. --- v2rayN/ServiceLib/Common/FileManager.cs | 24 ++++++++ v2rayN/ServiceLib/Global.cs | 2 + v2rayN/ServiceLib/Handler/CoreAdminHandler.cs | 50 +++++++-------- .../Handler/SysProxy/ProxySettingLinux.cs | 9 +-- .../Handler/SysProxy/ProxySettingOSX.cs | 9 +-- .../ServiceLib/Sample/kill_as_sudo_linux_sh | 61 +++++++++++++++++++ v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh | 56 +++++++++++++++++ v2rayN/ServiceLib/ServiceLib.csproj | 2 + .../Views/SudoPasswordInputView.axaml.cs | 3 +- 9 files changed, 170 insertions(+), 46 deletions(-) create mode 100644 v2rayN/ServiceLib/Sample/kill_as_sudo_linux_sh create mode 100644 v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh diff --git a/v2rayN/ServiceLib/Common/FileManager.cs b/v2rayN/ServiceLib/Common/FileManager.cs index d988c702..6d4d28ca 100644 --- a/v2rayN/ServiceLib/Common/FileManager.cs +++ b/v2rayN/ServiceLib/Common/FileManager.cs @@ -223,4 +223,28 @@ public static class FileManager // ignored } } + + /// + /// Creates a Linux shell file with the specified contents. + /// + /// + /// + /// + /// + public static async Task CreateLinuxShellFile(string fileName, string contents, bool overwrite) + { + var shFilePath = Utils.GetBinConfigPath(fileName); + + // Check if the file already exists and if we should overwrite it + if (!overwrite && File.Exists(shFilePath)) + { + return shFilePath; + } + + File.Delete(shFilePath); + await File.WriteAllTextAsync(shFilePath, contents); + await Utils.SetLinuxChmod(shFilePath); + + return shFilePath; + } } diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 5592fa2e..0728b2ea 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -38,6 +38,8 @@ public class Global public const string PacFileName = NamespaceSample + "pac"; public const string ProxySetOSXShellFileName = NamespaceSample + "proxy_set_osx_sh"; public const string ProxySetLinuxShellFileName = NamespaceSample + "proxy_set_linux_sh"; + public const string KillAsSudoOSXShellFileName = NamespaceSample + "kill_as_sudo_osx_sh"; + public const string KillAsSudoLinuxShellFileName = NamespaceSample + "kill_as_sudo_linux_sh"; public const string DefaultSecurity = "auto"; public const string DefaultNetwork = "tcp"; diff --git a/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs b/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs index 0adc6c41..ec448b7a 100644 --- a/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreAdminHandler.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using System.Text; using CliWrap; +using CliWrap.Buffered; namespace ServiceLib.Handler; @@ -11,6 +12,7 @@ public class CoreAdminHandler private Config _config; private Action? _updateFunc; private int _linuxSudoPid = -1; + private const string _tag = "CoreAdminHandler"; public async Task Init(Config config, Action updateFunc) { @@ -31,8 +33,11 @@ public class CoreAdminHandler public async Task RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath) { + StringBuilder sb = new(); + sb.AppendLine("#!/bin/bash"); var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}"; - var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh"); + sb.AppendLine($"sudo -S {cmdLine}"); + var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true); Process proc = new() { @@ -87,35 +92,24 @@ public class CoreAdminHandler return; } - var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}"; - var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh"); + try + { + var shellFileName = Utils.IsOSX() ? Global.KillAsSudoOSXShellFileName : Global.KillAsSudoLinuxShellFileName; + var shFilePath = await FileManager.CreateLinuxShellFile("kill_as_sudo.sh", EmbedUtils.GetEmbedText(shellFileName), true); - await Cli.Wrap(shFilePath) - .WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd)) - .ExecuteAsync(); + var arg = new List() { "-c", $"sudo -S {shFilePath} {_linuxSudoPid}" }; + var result = await Cli.Wrap(Global.LinuxBash) + .WithArguments(arg) + .WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd)) + .ExecuteBufferedAsync(); + + UpdateFunc(false, result.StandardOutput.ToString()); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } _linuxSudoPid = -1; } - - 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/SysProxy/ProxySettingLinux.cs b/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs index 9c5a53a8..9c2be056 100644 --- a/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs +++ b/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingLinux.cs @@ -18,14 +18,7 @@ public class ProxySettingLinux private static async Task ExecCmd(List args) { - var fileName = Utils.GetBinConfigPath(_proxySetFileName); - if (!File.Exists(fileName)) - { - var contents = EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName); - await File.AppendAllTextAsync(fileName, contents); - - await Utils.SetLinuxChmod(fileName); - } + var fileName = await FileManager.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName), false); await Utils.GetCliWrapOutput(fileName, args); } diff --git a/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs b/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs index c18cd728..9d15839b 100644 --- a/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs +++ b/v2rayN/ServiceLib/Handler/SysProxy/ProxySettingOSX.cs @@ -23,14 +23,7 @@ public class ProxySettingOSX private static async Task ExecCmd(List args) { - var fileName = Utils.GetBinConfigPath(_proxySetFileName); - if (!File.Exists(fileName)) - { - var contents = EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName); - await File.AppendAllTextAsync(fileName, contents); - - await Utils.SetLinuxChmod(fileName); - } + var fileName = await FileManager.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName), false); await Utils.GetCliWrapOutput(fileName, args); } diff --git a/v2rayN/ServiceLib/Sample/kill_as_sudo_linux_sh b/v2rayN/ServiceLib/Sample/kill_as_sudo_linux_sh new file mode 100644 index 00000000..7f62a532 --- /dev/null +++ b/v2rayN/ServiceLib/Sample/kill_as_sudo_linux_sh @@ -0,0 +1,61 @@ +#!/bin/bash +# +# Process Terminator Script for Linux +# This script forcibly terminates a process and all its child processes +# + +# Check if PID argument is provided +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +PID=$1 + +# Validate that input is a valid PID (numeric) +if ! [[ "$PID" =~ ^[0-9]+$ ]]; then + echo "Error: The PID must be a numeric value" + exit 1 +fi + +# Check if the process exists +if ! ps -p $PID > /dev/null; then + echo "Warning: No process found with PID $PID" + exit 0 +fi + +# Recursive function to find and kill all child processes +kill_children() { + local parent=$1 + local children=$(ps -o pid --no-headers --ppid "$parent") + + # Output information about processes being terminated + echo "Processing children of PID: $parent..." + + # Process each child + for child in $children; do + # Recursively find and kill child's children first + kill_children "$child" + + # Force kill the child process + echo "Terminating child process: $child" + kill -9 "$child" 2>/dev/null || true + done +} + +echo "============================================" +echo "Starting termination of process $PID and all its children" +echo "============================================" + +# Find and kill all child processes +kill_children "$PID" + +# Finally kill the main process +echo "Terminating main process: $PID" +kill -9 "$PID" 2>/dev/null || true + +echo "============================================" +echo "Process $PID and all its children have been terminated" +echo "============================================" + +exit 0 diff --git a/v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh b/v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh new file mode 100644 index 00000000..043d3703 --- /dev/null +++ b/v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Process Terminator Script for macOS +# This script forcibly terminates a process and all its descendant processes +# + +# Check if PID argument is provided +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +PID=$1 + +# Validate that input is a valid PID (numeric) +if ! [[ "$PID" =~ ^[0-9]+$ ]]; then + echo "Error: The PID must be a numeric value" + exit 1 +fi + +# Check if the process exists +if ! ps -p $PID > /dev/null; then + echo "Warning: No process found with PID $PID" + exit 0 +fi + +# Recursive function to find and kill all descendant processes +kill_descendants() { + local parent=$1 + # Use ps -eo pid,ppid for macOS compatibility + local children=$(ps -eo pid=,ppid= | awk -v ppid=$parent '$2==ppid {print $1}') + + echo "Processing children of PID: $parent..." + for child in $children; do + kill_descendants "$child" + echo "Terminating child process: $child" + kill -9 "$child" 2>/dev/null || true + done +} + +echo "============================================" +echo "Starting termination of process $PID and all its descendants" +echo "============================================" + +# Find and kill all descendant processes +kill_descendants "$PID" + +# Finally kill the main process +echo "Terminating main process: $PID" +kill -9 "$PID" 2>/dev/null || true + +echo "============================================" +echo "Process $PID and all its descendants have been terminated" +echo "============================================" + +exit 0 diff --git a/v2rayN/ServiceLib/ServiceLib.csproj b/v2rayN/ServiceLib/ServiceLib.csproj index bedde615..ecbab780 100644 --- a/v2rayN/ServiceLib/ServiceLib.csproj +++ b/v2rayN/ServiceLib/ServiceLib.csproj @@ -28,6 +28,8 @@ + + diff --git a/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs index e0b27607..9a5e908f 100644 --- a/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs @@ -66,8 +66,7 @@ public partial class SudoPasswordInputView : UserControl { // Use sudo echo command to verify password var arg = new List() { "-c", "sudo -S echo SUDO_CHECK" }; - var result = await CliWrap.Cli - .Wrap(Global.LinuxBash) + var result = await CliWrap.Cli.Wrap(Global.LinuxBash) .WithArguments(arg) .WithStandardInputPipe(CliWrap.PipeSource.FromString(password)) .ExecuteBufferedAsync();