1
0

orphans_linux.go 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. //go:build linux
  2. package tuic
  3. import (
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strconv"
  8. "strings"
  9. "syscall"
  10. "time"
  11. )
  12. func killStrayTuicProcesses(binaryPath string) int {
  13. base := filepath.Base(binaryPath)
  14. if base == "" || base == "." || base == string(filepath.Separator) {
  15. return 0
  16. }
  17. configDir := filepath.Clean(ConfigDir())
  18. self := os.Getpid()
  19. entries, err := os.ReadDir("/proc")
  20. if err != nil {
  21. return 0
  22. }
  23. killed := 0
  24. for _, e := range entries {
  25. pid, err := strconv.Atoi(e.Name())
  26. if err != nil || pid == self {
  27. continue
  28. }
  29. if procExeBase(pid) != base && cmdlineArgv0Base(pid) != base {
  30. continue
  31. }
  32. if !isManagedTuicCmdline(pid, configDir) {
  33. continue
  34. }
  35. if err := syscall.Kill(pid, syscall.SIGTERM); err == nil {
  36. killed++
  37. time.Sleep(50 * time.Millisecond)
  38. if err := syscall.Kill(pid, 0); err == nil {
  39. _ = syscall.Kill(pid, syscall.SIGKILL)
  40. }
  41. }
  42. }
  43. return killed
  44. }
  45. func isManagedTuicCmdline(pid int, configDir string) bool {
  46. data, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
  47. if err != nil || len(data) == 0 {
  48. return false
  49. }
  50. args := strings.Split(string(data), "\x00")
  51. for i, arg := range args {
  52. if arg == "-c" && i+1 < len(args) {
  53. cfg := filepath.Clean(args[i+1])
  54. if strings.HasPrefix(cfg, configDir) {
  55. return true
  56. }
  57. }
  58. }
  59. return false
  60. }
  61. func procExeBase(pid int) string {
  62. exe, err := os.Readlink(fmt.Sprintf("/proc/%d/exe", pid))
  63. if err != nil {
  64. return ""
  65. }
  66. return filepath.Base(exe)
  67. }
  68. func cmdlineArgv0Base(pid int) string {
  69. data, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
  70. if err != nil || len(data) == 0 {
  71. return ""
  72. }
  73. first := data
  74. for i, b := range data {
  75. if b == 0 {
  76. first = data[:i]
  77. break
  78. }
  79. }
  80. return filepath.Base(string(first))
  81. }