eth2net.go 5.7 KB

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