之前尝试通过修改分区的方式来实现光猫开机自启程序,但是因为分区无法写入最终告一段落,现在换一个思路。

通过一台设备监测光猫接口变动,自动开启telent并执行特定脚本拉起其他脚本

package main

import (
        "encoding/json"
        "flag"
        "fmt"
        "io"
        "log"
        "net"
        "net/http"
        "os"
        "regexp"
        "strings"
        "time"

        "github.com/google/goexpect"
        "github.com/vishvananda/netlink"
)

// Config struct to hold the configuration
type Config struct {
        Address    string `json:"address"`
        User       string `json:"user"`
        Pass       string `json:"pass"`
        Cmd        string `json:"cmd"`
        TelnetPort string `json:"telnet_port"`
        TelnetURL  string `json:"telnet_url"`
        UserRE     string `json:"user_re"`
        PassRE     string `json:"pass_re"`
        PromptRE   string `json:"prompt_re"`
}

// Default configuration values
var defaultConfig = Config{
        Address:    "192.168.1.1",
        User:       "admin",
        Pass:       "password123",
        Cmd:        "show version",
        TelnetPort: "23",
        TelnetURL:  "http://192.168.1.1/cgi-bin/telnetenable.cgi?telnetenable=1&key=D078800E0CD0",
        UserRE:     "login:",
        PassRE:     "Password:",
        PromptRE:   "admin@*",
}

// Function to load configuration from a JSON file
func loadConfig(filename string) (*Config, error) {
        // File doesn't exist, create it and write default values
        if _, err := os.Stat(filename); os.IsNotExist(err) {
                log.Println("Config file not found, creating a new one with default values.")
                file, err := os.Create(filename)
                if err != nil {
                        return nil, fmt.Errorf("failed to create config file: %v", err)
                }
                defer file.Close()

                encoder := json.NewEncoder(file)
                encoder.SetIndent("", "  ")
                encoder.SetEscapeHTML(false)
                if err := encoder.Encode(defaultConfig); err != nil {
                        return nil, fmt.Errorf("failed to write default config to file: %v", err)
                }

                log.Printf("Default config written to %s", filename)
                os.Exit(0)
        }

        // If the file exists, read and parse it
        file, err := os.Open(filename)
        if err != nil {
                return nil, fmt.Errorf("failed to open config file: %v", err)
        }
        defer file.Close()

        var config Config
        if err := json.NewDecoder(file).Decode(&config); err != nil {
                return nil, fmt.Errorf("parse config file: %v", err)
        }

        return &config, nil
}

func enableTelnet(url string) error {
        ip := strings.Split(strings.Split(url, "://")[1], "/")[0]
        if strings.Contains(ip, ":") {
                ip = strings.Split(ip, ":")[0]
        }

        for {
                if _, err := net.DialTimeout("tcp", ip+":80", 2*time.Second); err == nil {
                        break
                }
                log.Printf("Unable to connect to %s, retrying...", ip)
                time.Sleep(10 * time.Second)
        }

        resp, err := http.Get(url)
        if err != nil {
                return fmt.Errorf("telnet enable request failed: %v", err)
        }
        defer resp.Body.Close()

        buf, err := io.ReadAll(resp.Body)
        if err != nil {
                return fmt.Errorf("read response failed: %v", err)
        }

        if !strings.Contains(string(buf), "telnet开启") {
                return fmt.Errorf("telnet enable failed")
        }
        log.Println("Telnet enabled successfully")
        return nil
}

func runExpect(config *Config) error {
        const timeout = 60 * time.Second
        // Compile regular expressions
        userRE, err := regexp.Compile(config.UserRE)
        if err != nil {
                return fmt.Errorf("compile user RE: %v", err)
        }
        passRE, err := regexp.Compile(config.PassRE)
        if err != nil {
                return fmt.Errorf("compile pass RE: %v", err)
        }
        promptRE, err := regexp.Compile(config.PromptRE)
        if err != nil {
                return fmt.Errorf("compile prompt RE: %v", err)
        }
        for {
                e, _, err := expect.Spawn(fmt.Sprintf("telnet %s %s", config.Address, config.TelnetPort), -1)
                if err != nil {
                        log.Fatal(err)
                }
                defer e.Close()

                e.Expect(userRE, timeout)
                e.Send(config.User + "\n")
                e.Expect(passRE, timeout)
                e.Send(config.Pass + "\n")
                e.Expect(promptRE, timeout)
                e.Send(config.Cmd + "\n")
                result, _, _ := e.Expect(promptRE, timeout)
                e.Send("exit\n")

                fmt.Println(result)
                log.Println("Command run successfully")
                return nil
        }
}

func monitorNetwork(config *Config, iface string) {
        linkUpdates := make(chan netlink.LinkUpdate)
        done := make(chan struct{})

        if err := netlink.LinkSubscribe(linkUpdates, done); err != nil {
                log.Fatalf("Failed to subscribe to netlink updates: %v", err)
        }

        for update := range linkUpdates {
                if update.Attrs().Name != iface || update.Attrs().Flags&net.FlagUp == 0 {
                        continue
                }

                log.Println("Network change detected, running commands...")
                if err := enableTelnet(config.TelnetURL); err != nil {
                        log.Printf("Telnet error: %v", err)
                } else if err := runExpect(config); err != nil {
                        log.Printf("Expect error: %v", err)
                }
        }
}

func main() {
        iface := flag.String("iface", "", "Network interface to monitor")
        daemon := flag.Bool("d", false, "Run in daemon mode")
        configFile := flag.String("c", "config.json", "Configuration file path")
        flag.Parse()

        config, err := loadConfig(*configFile)
        if err != nil {
                log.Fatal(err)
        }

        if err := enableTelnet(config.TelnetURL); err != nil {
                log.Fatal(err)
        }
        if err := runExpect(config); err != nil {
                log.Fatal(err)
        }

        if *daemon {
                if *iface == "" {
                        log.Fatal("Network interface must be specified in daemon mode using -iface flag")
                }
                log.Printf("Running in daemon mode, monitoring interface: %s", *iface)
                go monitorNetwork(config, *iface)
                select {}
        }
}

代码基本由ai编写,个人仅提供思路和修改报错

GOARCH=arm64 GOOS=linux go build -ldflags "-s -w" -trimpath
{
  "address": "192.168.123.1",
  "user": "admin",
  "pass": "Fh@0E0CD0",
  "cmd": "/osgi/custom/main.sh",
  "telnet_port": "23",
  "telnet_url": "http://192.168.123.1/cgi-bin/telnetenable.cgi?telnetenable=1&key=D078800E0CD0",
  "user_re": "login:",
  "pass_re": "Password:",
  "prompt_re": "admin@*"
}