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();