usb0net.go 6.0 KB

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