eth2net.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // Author: NiuJiuRu
  2. // Email: niujiuru@qq.com
  3. // 以下函数需"root"权限运行
  4. package air720u
  5. import (
  6. "fmt"
  7. "io/fs"
  8. "net"
  9. "os"
  10. "os/exec"
  11. "path/filepath"
  12. "strconv"
  13. "strings"
  14. "syscall"
  15. "time"
  16. "github.com/sirupsen/logrus"
  17. "hnyfkj.com.cn/rtu/linux/baseapp"
  18. )
  19. // 只保留需要使用的"eth2"网卡
  20. func disableEthBut2() error {
  21. ifaces, err := net.Interfaces()
  22. if err != nil {
  23. return fmt.Errorf("获取网络接口列表失败: %w", err)
  24. }
  25. for _, iface := range ifaces {
  26. if !strings.HasPrefix(iface.Name, "eth") || iface.Name == "eth2" {
  27. continue
  28. }
  29. cmd := exec.Command("ip", "link", "set", "dev", iface.Name, "down")
  30. cmd.Stdout = baseapp.Logger.WriterLevel(logrus.DebugLevel)
  31. cmd.Stderr = baseapp.Logger.WriterLevel(logrus.ErrorLevel)
  32. if err := cmd.Run(); err != nil {
  33. return fmt.Errorf("关闭网口\"%s\"失败: %w", iface.Name, err)
  34. }
  35. }
  36. return nil
  37. }
  38. // 启动DHCP客户端, 请求IP地址
  39. func dialupEth2() error {
  40. cmd1 := exec.Command("ip", "link", "set", "dev", "eth2", "up")
  41. cmd1.Stdout = baseapp.Logger.WriterLevel(logrus.DebugLevel)
  42. cmd1.Stderr = baseapp.Logger.WriterLevel(logrus.ErrorLevel)
  43. if err := cmd1.Run(); err != nil {
  44. return fmt.Errorf("启动网口\"eth2\"失败: %w", err)
  45. }
  46. eth2pid := baseapp.RUN_DIR + "/udhcpc.eth2.pid"
  47. cmd2 := exec.Command("udhcpc", "-b", "-i", "eth2", "-p", eth2pid)
  48. cmd2.Stdout = baseapp.Logger.WriterLevel(logrus.DebugLevel)
  49. cmd2.Stderr = baseapp.Logger.WriterLevel(logrus.ErrorLevel)
  50. if err := cmd2.Run(); err != nil {
  51. return fmt.Errorf("\"eth2\"请求地址失败: %w", err)
  52. }
  53. return nil
  54. }
  55. // 检测"eth2"网口上的"udhcpc"后台进程是否正在运行, 只保持一个实例
  56. func udhcpcEth2Exists() (bool, error) {
  57. eth2pid := baseapp.RUN_DIR + "/udhcpc.eth2.pid"
  58. data, err := os.ReadFile(eth2pid)
  59. if err != nil {
  60. return false, err
  61. }
  62. pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
  63. if err != nil {
  64. return false, err
  65. }
  66. err = syscall.Kill(pid, 0)
  67. if err == nil { // 存在
  68. return true, nil
  69. }
  70. if err == syscall.ESRCH { // 不存在
  71. return false, nil
  72. }
  73. return false, err
  74. }
  75. // 杀死"eth2"网口上的"udhcpc"进程(根据上次运行时它的PID-文件记录)
  76. func killEth2Udhcpc() error {
  77. eth2pid := baseapp.RUN_DIR + "/udhcpc.eth2.pid"
  78. data, err := os.ReadFile(eth2pid)
  79. if err != nil {
  80. return err
  81. }
  82. pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
  83. if err != nil {
  84. return err
  85. }
  86. if err := syscall.Kill(pid, syscall.SIGTERM); err != nil && err != syscall.ESRCH {
  87. syscall.Kill(pid, syscall.SIGKILL)
  88. }
  89. return nil
  90. }
  91. // 强制杀死所有运行中的"udhcpc"进程,包括驻留在后台运行的(SIGKILL)
  92. func killAllUdhcpc() {
  93. cmd := exec.Command("sh", "-c", `ps | grep '[u]dhcpc' | awk '{print $1}'`)
  94. output, err := cmd.Output()
  95. if err != nil {
  96. return // 没找到也不是错误
  97. }
  98. pids := strings.FieldsSeq(string(output))
  99. for pidStr := range pids {
  100. pid, err := strconv.Atoi(pidStr)
  101. if err != nil {
  102. continue
  103. }
  104. if err := syscall.Kill(pid, syscall.SIGTERM); err != nil && err != syscall.ESRCH {
  105. syscall.Kill(pid, syscall.SIGKILL)
  106. }
  107. }
  108. }
  109. // 运行"udhcpc"后, 获取"eth2"网口分配到的IPv4地址, 并校检其合法性
  110. func getEth2Addr() (ip, mask string, err error) {
  111. iface, err := net.InterfaceByName("eth2")
  112. if err != nil {
  113. return "", "", fmt.Errorf("找不到网络接口\"eth2\": %w", err)
  114. }
  115. addrs, err := iface.Addrs()
  116. if err != nil {
  117. return "", "", fmt.Errorf("获取\"eth2\"的地址失败: %w", err)
  118. }
  119. for _, addr := range addrs {
  120. ipNet, ok := addr.(*net.IPNet)
  121. if !ok {
  122. continue
  123. }
  124. ip4 := ipNet.IP.To4()
  125. if ip4 == nil {
  126. continue
  127. }
  128. if ip4.IsPrivate() { // 判断得到IP地址是否合法
  129. return ip4.String(), net.IP(ipNet.Mask).String(), nil
  130. } else {
  131. return "", "", fmt.Errorf("分配给\"eth2\"的地址\"%s\"无效", ip4.String())
  132. }
  133. }
  134. return "", "", fmt.Errorf("在\"eth2\"上未找到有效的IPv4 地址")
  135. }
  136. // 查找并杀死所有占用"/dev/ttyUSB0"设备的进程,返回被杀死进程的PID
  137. func freeTTYUSB0() ([]int, error) {
  138. var pids []int
  139. seen := make(map[int]struct{})
  140. err := filepath.WalkDir("/proc", func(path string, d fs.DirEntry, walkErr error) error {
  141. if walkErr != nil {
  142. return nil
  143. }
  144. if filepath.Base(filepath.Dir(path)) != "fd" {
  145. return nil
  146. }
  147. link, err := os.Readlink(path)
  148. if err != nil || link != "/dev/ttyUSB0" {
  149. return nil
  150. }
  151. parts := strings.Split(path, "/")
  152. if len(parts) < 3 {
  153. return nil
  154. }
  155. pid, err := strconv.Atoi(parts[2])
  156. if err != nil {
  157. return nil
  158. }
  159. if _, exists := seen[pid]; exists {
  160. return nil // 已处理过这个PID, 跳过
  161. }
  162. seen[pid] = struct{}{}
  163. if err := syscall.Kill(pid, syscall.SIGTERM); err != nil && err != syscall.ESRCH {
  164. syscall.Kill(pid, syscall.SIGKILL)
  165. }
  166. pids = append(pids, pid)
  167. return nil
  168. })
  169. return pids, err
  170. }
  171. // 定时检测"eth2"网卡上的"udhcpc"后台服务进程, 发现退出时自动拉起
  172. func monitorEth2Udhcpc(exitCh <-chan struct{}) {
  173. t := time.NewTicker(time.Duration(5) * time.Second)
  174. defer t.Stop()
  175. for {
  176. select {
  177. case <-t.C:
  178. bExists, _ := udhcpcEth2Exists()
  179. if !bExists { // 清场一次
  180. killAllUdhcpc()
  181. disableEthBut2()
  182. }
  183. if !bExists && dialupEth2() == nil { // 重新进场
  184. ip, mask, _ := getEth2Addr()
  185. baseapp.Logger.Warnf("[%s] \"eth2\"重新分配的地址: %s/%s!", MODULE_NAME, ip, mask)
  186. }
  187. case <-exitCh:
  188. return
  189. } // select end
  190. } // for end
  191. }