| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- package main
- import (
- "bufio"
- "context"
- "errors"
- "fmt"
- "io"
- "os"
- "os/signal"
- "strings"
- "sync/atomic"
- "syscall"
- "time"
- "github.com/google/uuid"
- "hnyfkj.com.cn/rtu/linux/baseapp"
- )
- const MODULE_NAME = "YFKJ_SSH_CLIENT"
- var (
- coupler *MQTTCoupler
- Version = "1.0.0.1"
- ErrBrokerAddressEmpty = errors.New("mqtt server address is empty")
- ErrIMEINotAvailable = errors.New("device imei is not available")
- )
- func main() {
- if baseapp.IsArgsParam("-h") {
- help()
- return
- }
- if baseapp.IsArgsParam("-v") {
- fmt.Println("程序版本:", Version, "\n构建时间:", baseapp.BuildTime)
- return
- }
- devIMEI := baseapp.GetArgsParamStr("-c", "")
- if devIMEI == "" {
- help()
- return
- }
- if err := loadAppConfig(); err != nil {
- fmt.Printf("[%s] 错误: %v!!\n", MODULE_NAME, err)
- return
- }
- if CfgServers.MQTTSrv.Address == "" {
- fmt.Printf("[%s] 错误: %v!!\n", MODULE_NAME, ErrBrokerAddressEmpty)
- return
- }
- ctx, cancel := context.WithCancel(context.Background())
- coupler = &MQTTCoupler{
- ctx: ctx,
- cancel: cancel,
- broker: CfgServers.MQTTSrv.Address,
- username: CfgServers.MQTTSrv.Username,
- password: CfgServers.MQTTSrv.Password,
- clientID: uuid.New().String(),
- imei: devIMEI,
- cwd: "/",
- }
- if err := coupler.init2(); err != nil {
- fmt.Printf("[%s] 错误: %v\n!!", MODULE_NAME, err)
- return
- }
- term() // 启动终端模拟器卍
- }
- // SHELL终端模拟器
- // 1, 连接远程设备, 是否成功
- // 2, 等待用户输入, 封装请求
- // 3, 没有用户输入, 封装心跳
- // 4, 发送请求数据, 远程执行
- // 5, 耗时用户请求, 允许中断, Ctrl+C
- // 6, 等待返回结果, 打印输出
- // 7, 循环等待下次, 直到退出
- func term() {
- var pingState atomic.Bool
- var executing atomic.Bool
- // -在线-心跳检测
- heartbeatLoop(&pingState)
- // Ctrl+C中断处理
- interruptLoop(&executing)
- reader := bufio.NewReader(os.Stdin)
- for {
- if !pingState.Load() {
- fmt.Printf("[%s] 无法连接目标设备!!\n", MODULE_NAME)
- break
- }
- fmt.Print("\033[?25h") // 显示光标
- fmt.Printf("root@%s:%s# ", coupler.imei, coupler.cwd)
- input, err := reader.ReadString('\n')
- if err != nil {
- if err == io.EOF {
- os.Exit(0)
- }
- fmt.Println("读取用户输入失败:", err)
- continue
- }
- input = strings.TrimSpace(input)
- if input == "" {
- continue
- }
- switch input {
- case "quit":
- _, _ = coupler.quit()
- os.Exit(0)
- }
- fmt.Print("\033[?25l") // 隐藏光标
- executing.Store(true)
- result, err := coupler.exec(input)
- executing.Store(false)
- if err != nil {
- fmt.Printf("执行错误: %v\n", err)
- continue
- }
- if result.Stdout != "" {
- fmt.Println(result.Stdout)
- }
- if result.Stderr != "" {
- fmt.Fprintln(os.Stderr, result.Stderr)
- }
- if result.Cwd != "" {
- coupler.cwd = result.Cwd
- }
- }
- }
- func help() {
- h := `
- -h 显示帮助提示
- -v 当前程序版本
- -c 连接目标设备(IMEI), 例如: -c 869523059113051
- `
- fmt.Println(h)
- }
- func interruptLoop(executing *atomic.Bool) {
- sigCh := make(chan os.Signal, 1)
- signal.Notify(sigCh, syscall.SIGINT)
- go func() {
- for range sigCh {
- if executing.Load() {
- _, _ = coupler.stop()
- }
- fmt.Print("\n^C\n")
- } // end for
- }()
- }
- func heartbeatLoop(pingState *atomic.Bool) {
- go func() {
- ticker := time.NewTicker(1 * time.Second)
- defer ticker.Stop()
- for range ticker.C {
- resp, err := coupler.ping()
- if err == nil && resp.Error == nil && resp.Result != nil && string(resp.Result) == "pong" {
- pingState.Store(true)
- } else {
- pingState.Store(false)
- } // end if
- } // end for
- }()
- }
|