usb0net.go 6.2 KB

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