| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- // Author: NiuJiuRu
- // Email: niujiuru@qq.com
- // 以下函数需"root"权限运行
- package ec200u
- import (
- "fmt"
- "io/fs"
- "net"
- "os"
- "os/exec"
- "path/filepath"
- "strconv"
- "strings"
- "syscall"
- "time"
- "github.com/sirupsen/logrus"
- "github.com/vishvananda/netlink"
- "hnyfkj.com.cn/rtu/linux/baseapp"
- )
- // 禁用"eth0"网口, 使其不可用
- func disableEth0() error {
- iface, err := net.InterfaceByName("eth0")
- if err != nil {
- return fmt.Errorf("未找到网口\"eth0\": %w", err)
- }
- cmd := exec.Command("ip", "link", "set", "dev", iface.Name, "down")
- cmd.Stdout = baseapp.Logger.WriterLevel(logrus.DebugLevel)
- cmd.Stderr = baseapp.Logger.WriterLevel(logrus.ErrorLevel)
- if err := cmd.Run(); err != nil {
- return fmt.Errorf("关闭网口\"%s\"失败: %w", iface.Name, err)
- }
- return nil
- }
- // 启动"usb0"网口, 使其变可用
- func enableUSB0() error {
- cmd := exec.Command("ip", "link", "set", "dev", "usb0", "up")
- cmd.Stdout = baseapp.Logger.WriterLevel(logrus.DebugLevel)
- cmd.Stderr = baseapp.Logger.WriterLevel(logrus.ErrorLevel)
- if err := cmd.Run(); err != nil {
- return fmt.Errorf("启动网口\"usb0\"失败: %w", err)
- }
- return nil
- }
- // 启动DHCP客户端, 请求IP地址
- func dialupUSB0() error {
- if err := enableUSB0(); err != nil {
- return err
- }
- usb0pid := baseapp.RUN_DIR + "/udhcpc.usb0.pid"
- cmd := exec.Command("udhcpc", "-b", "-i", "usb0", "-p", usb0pid)
- cmd.Stdout = baseapp.Logger.WriterLevel(logrus.DebugLevel)
- cmd.Stderr = baseapp.Logger.WriterLevel(logrus.ErrorLevel)
- if err := cmd.Run(); err != nil {
- return fmt.Errorf("\"usb0\"请求地址失败: %w", err)
- }
- return nil
- }
- // 检测"usb0"网口上的"udhcpc"后台进程是否正在运行, 只保持一个实例
- func udhcpcUSB0Exists() (bool, error) {
- usb0pid := baseapp.RUN_DIR + "/udhcpc.usb0.pid"
- data, err := os.ReadFile(usb0pid)
- if err != nil {
- return false, err
- }
- pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
- if err != nil {
- return false, err
- }
- err = syscall.Kill(pid, 0)
- if err == nil { // 存在
- return true, nil
- }
- if err == syscall.ESRCH { // 不存在
- return false, nil
- }
- return false, err
- }
- // 杀死"usb0"网口上的"udhcpc"进程(根据上次运行时它的PID-文件记录)
- func killUSB0Udhcpc() error {
- usb0pid := baseapp.RUN_DIR + "/udhcpc.usb0.pid"
- data, err := os.ReadFile(usb0pid)
- if err != nil {
- return err
- }
- pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
- if err != nil {
- return err
- }
- if err := syscall.Kill(pid, syscall.SIGTERM); err != nil && err != syscall.ESRCH {
- syscall.Kill(pid, syscall.SIGKILL)
- }
- return nil
- }
- // 强制杀死所有运行中的"udhcpc"进程,包括驻留在后台运行的(SIGKILL)
- func killAllUdhcpc() {
- cmd := exec.Command("sh", "-c", `ps | grep '[u]dhcpc' | awk '{print $1}'`)
- output, err := cmd.Output()
- if err != nil {
- return // 没找到也不是错误
- }
- pids := strings.FieldsSeq(string(output))
- for pidStr := range pids {
- pid, err := strconv.Atoi(pidStr)
- if err != nil {
- continue
- }
- if err := syscall.Kill(pid, syscall.SIGTERM); err != nil && err != syscall.ESRCH {
- syscall.Kill(pid, syscall.SIGKILL)
- }
- }
- }
- // 运行"udhcpc"后, 获取"usb0"网口分配到的IPv4地址, 并校检其合法性
- func getUSB0Addr() (ip, mask string, err error) {
- iface, err := net.InterfaceByName("usb0")
- if err != nil {
- return "", "", fmt.Errorf("找不到网络接口\"usb0\": %w", err)
- }
- addrs, err := iface.Addrs()
- if err != nil {
- return "", "", fmt.Errorf("获取\"usb0\"的地址失败: %w", err)
- }
- for _, addr := range addrs {
- ipNet, ok := addr.(*net.IPNet)
- if !ok {
- continue
- }
- ip4 := ipNet.IP.To4()
- if ip4 == nil {
- continue
- }
- if ip4.IsPrivate() { // 判断得到IP地址是否合法
- return ip4.String(), net.IP(ipNet.Mask).String(), nil
- } else {
- return "", "", fmt.Errorf("分配给\"usb0\"的地址\"%s\"无效", ip4.String())
- }
- }
- return "", "", fmt.Errorf("在\"usb0\"上未找到有效的IPv4 地址")
- }
- // 查找并杀死所有占用"/dev/ttyUSB0"设备的进程,返回被杀死进程的PID
- func freeTTYUSB0() ([]int, error) {
- var pids []int
- seen := make(map[int]struct{})
- err := filepath.WalkDir("/proc", func(path string, d fs.DirEntry, walkErr error) error {
- if walkErr != nil {
- return nil
- }
- if filepath.Base(filepath.Dir(path)) != "fd" {
- return nil
- }
- link, err := os.Readlink(path)
- if err != nil || link != "/dev/ttyUSB0" {
- return nil
- }
- parts := strings.Split(path, "/")
- if len(parts) < 3 {
- return nil
- }
- pid, err := strconv.Atoi(parts[2])
- if err != nil {
- return nil
- }
- if _, exists := seen[pid]; exists {
- return nil // 已处理过这个PID, 跳过
- }
- seen[pid] = struct{}{}
- if err := syscall.Kill(pid, syscall.SIGTERM); err != nil && err != syscall.ESRCH {
- syscall.Kill(pid, syscall.SIGKILL)
- }
- pids = append(pids, pid)
- return nil
- })
- return pids, err
- }
- // 定时检测"usb0"网卡上的"udhcpc"后台服务进程, 发现退出时自动拉起
- func monitorUSB0Udhcpc(exitCh <-chan struct{}) {
- t := time.NewTicker(time.Duration(5) * time.Second)
- defer t.Stop()
- for {
- select {
- case <-t.C:
- bExists, _ := udhcpcUSB0Exists()
- if !bExists { // 清场一次
- killAllUdhcpc()
- disableEth0()
- }
- if !bExists && dialupUSB0() == nil { // 重新进场
- ip, mask, _ := getUSB0Addr()
- baseapp.Logger.Warnf("[%s] \"usb0\"重新分配的地址: %s/%s!", MODULE_NAME, ip, mask)
- }
- case <-exitCh:
- return
- } // select end
- } // for end
- }
- func isInterfaceUp(name string) (bool, error) {
- link, err := netlink.LinkByName(name)
- if err != nil {
- return false, err
- }
- return link.Attrs().Flags&net.FlagUp != 0, nil
- }
- func Is4GCableConnected() (bool, error) {
- if up, _ := isInterfaceUp("usb0"); !up { // 管理状态
- err := enableUSB0()
- if err != nil {
- return false, err
- }
- }
- if data, err := os.ReadFile("/sys/class/net/usb0/carrier"); err == nil { // 物理状态
- return strings.TrimSpace(string(data)) == "1", nil
- }
- link, err := netlink.LinkByName("usb0")
- if err != nil {
- return false, err
- }
- return link.Attrs().OperState == netlink.OperUp, nil // 操作状态
- }
|