|
@@ -19,7 +19,7 @@ import (
|
|
|
const MODULE_NAME = "YFKJ_SSHD"
|
|
const MODULE_NAME = "YFKJ_SSHD"
|
|
|
|
|
|
|
|
var (
|
|
var (
|
|
|
- Coupler *MQTTCoupler
|
|
|
|
|
|
|
+ coupler *MQTTCoupler
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
const (
|
|
@@ -62,6 +62,7 @@ type executorState int
|
|
|
const (
|
|
const (
|
|
|
execIdle executorState = iota // 空闲状态时, 可安全回收
|
|
execIdle executorState = iota // 空闲状态时, 可安全回收
|
|
|
execRunning // 正在执行时, 不允许回收
|
|
execRunning // 正在执行时, 不允许回收
|
|
|
|
|
+ execClosing // 表明执行器正在关闭中..
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
type clientExecutor struct {
|
|
type clientExecutor struct {
|
|
@@ -80,7 +81,7 @@ func ModuleInit(mqttBroker, mqttUsername, mqttPassword string) bool {
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
|
|
|
|
- Coupler = &MQTTCoupler{
|
|
|
|
|
|
|
+ coupler = &MQTTCoupler{
|
|
|
broker: mqttBroker,
|
|
broker: mqttBroker,
|
|
|
username: mqttUsername,
|
|
username: mqttUsername,
|
|
|
password: mqttPassword,
|
|
password: mqttPassword,
|
|
@@ -95,19 +96,19 @@ func ModuleInit(mqttBroker, mqttUsername, mqttPassword string) bool {
|
|
|
registerRpcMeths: &singletask.OnceTask{},
|
|
registerRpcMeths: &singletask.OnceTask{},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if err := Coupler.init2(); err != nil {
|
|
|
|
|
|
|
+ if err := coupler.init2(); err != nil {
|
|
|
baseapp.Logger.Errorf("[%s] 初始化远程运维模块失败: %v!!", MODULE_NAME, err)
|
|
baseapp.Logger.Errorf("[%s] 初始化远程运维模块失败: %v!!", MODULE_NAME, err)
|
|
|
return false
|
|
return false
|
|
|
}
|
|
}
|
|
|
- go Coupler.startExecutorReaper(ExecutorCheckInterval, ExecutorTimeout)
|
|
|
|
|
- go Coupler.keepOnline()
|
|
|
|
|
|
|
+ go coupler.startExecutorReaper(ExecutorCheckInterval, ExecutorTimeout)
|
|
|
|
|
+ go coupler.keepOnline()
|
|
|
|
|
|
|
|
return true
|
|
return true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func ModuleExit() {
|
|
func ModuleExit() {
|
|
|
- if Coupler != nil {
|
|
|
|
|
- Coupler.cancel()
|
|
|
|
|
|
|
+ if coupler != nil {
|
|
|
|
|
+ coupler.cancel()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -226,9 +227,9 @@ func (c *MQTTCoupler) execOneCmd(msg mqtt.Message) {
|
|
|
str := string(msg.Payload())
|
|
str := string(msg.Payload())
|
|
|
baseapp.Logger.Infof("[%s] 收到一个RPC请求: %s", MODULE_NAME, str)
|
|
baseapp.Logger.Infof("[%s] 收到一个RPC请求: %s", MODULE_NAME, str)
|
|
|
|
|
|
|
|
- var resp *jsonrpc2.Response // 预定义一个空的应答
|
|
|
|
|
- var clientID string // 该客户端的唯一标识
|
|
|
|
|
- var ce *clientExecutor // 该客户端的指令执行器
|
|
|
|
|
|
|
+ var resp *jsonrpc2.Response // 预先定义一个空的应答
|
|
|
|
|
+ var clientID string // 该客户端的|唯一标识|
|
|
|
|
|
+ var ce *clientExecutor // 该客户端的本地执行器
|
|
|
var exists bool // 判断执行器是否已存在
|
|
var exists bool // 判断执行器是否已存在
|
|
|
|
|
|
|
|
req, err := jsonrpc2.ParseRequest(str)
|
|
req, err := jsonrpc2.ParseRequest(str)
|
|
@@ -260,15 +261,19 @@ func (c *MQTTCoupler) execOneCmd(msg mqtt.Message) {
|
|
|
}
|
|
}
|
|
|
c.executorMapMu.Unlock()
|
|
c.executorMapMu.Unlock()
|
|
|
|
|
|
|
|
- ce.mu.Lock() // 确保同一客户端(ID一样)的指令串行执行
|
|
|
|
|
|
|
+ ce.mu.Lock()
|
|
|
ce.lastPing = time.Now()
|
|
ce.lastPing = time.Now()
|
|
|
|
|
+ ce.mu.Unlock()
|
|
|
|
|
|
|
|
switch req.Method {
|
|
switch req.Method {
|
|
|
// Call-1: 心跳, 链路检测,"ping-pong"测试
|
|
// Call-1: 心跳, 链路检测,"ping-pong"测试
|
|
|
case "executor.ping":
|
|
case "executor.ping":
|
|
|
resp = buildResp(req, "pong", nil)
|
|
resp = buildResp(req, "pong", nil)
|
|
|
|
|
+ goto retp
|
|
|
// Call-2:在本地shell中执行远程下发的指令
|
|
// Call-2:在本地shell中执行远程下发的指令
|
|
|
case "executor.exec":
|
|
case "executor.exec":
|
|
|
|
|
+ ce.mu.Lock()
|
|
|
|
|
+
|
|
|
params, err := extractShellExecuteParams(req.Params)
|
|
params, err := extractShellExecuteParams(req.Params)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
ce.mu.Unlock()
|
|
ce.mu.Unlock()
|
|
@@ -276,48 +281,61 @@ func (c *MQTTCoupler) execOneCmd(msg mqtt.Message) {
|
|
|
goto retp
|
|
goto retp
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if ce.state == execClosing {
|
|
|
|
|
+ ce.mu.Unlock()
|
|
|
|
|
+ resp = jsonrpc2.BuildError(req, -32001, "executor closed")
|
|
|
|
|
+ goto retp
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ce.state == execRunning {
|
|
|
|
|
+ ce.mu.Unlock()
|
|
|
|
|
+ resp = jsonrpc2.BuildError(req, -32002, "executor busy")
|
|
|
|
|
+ goto retp
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
ce.state = execRunning
|
|
ce.state = execRunning
|
|
|
ce.mu.Unlock()
|
|
ce.mu.Unlock()
|
|
|
|
|
|
|
|
- result, err := ce.executor.Exec(params)
|
|
|
|
|
|
|
+ result, err := ce.executor.Exec(params) // 本地执行用户指令
|
|
|
|
|
|
|
|
ce.mu.Lock()
|
|
ce.mu.Lock()
|
|
|
- ce.state = execIdle
|
|
|
|
|
- ce.lastPing = time.Now()
|
|
|
|
|
|
|
+ if ce.state != execClosing {
|
|
|
|
|
+ ce.state = execIdle
|
|
|
|
|
+ ce.lastPing = time.Now()
|
|
|
|
|
+ }
|
|
|
ce.mu.Unlock()
|
|
ce.mu.Unlock()
|
|
|
|
|
|
|
|
resp = buildResp(req, result, err)
|
|
resp = buildResp(req, result, err)
|
|
|
goto retp
|
|
goto retp
|
|
|
// Call-3:中断本地shell的执行,等价Ctrl+C
|
|
// Call-3:中断本地shell的执行,等价Ctrl+C
|
|
|
case "executor.interrupt":
|
|
case "executor.interrupt":
|
|
|
- if ce.state != execRunning {
|
|
|
|
|
- resp = jsonrpc2.BuildError(req, -32001, "no running command")
|
|
|
|
|
- break
|
|
|
|
|
|
|
+ ce.mu.Lock()
|
|
|
|
|
+ running := (ce.state == execRunning)
|
|
|
|
|
+ ce.mu.Unlock()
|
|
|
|
|
+
|
|
|
|
|
+ if !running {
|
|
|
|
|
+ resp = jsonrpc2.BuildError(req, -32003, "no running command")
|
|
|
|
|
+ goto retp
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
err := ce.executor.Interrupt()
|
|
err := ce.executor.Interrupt()
|
|
|
resp = buildResp(req, "interrupted", err)
|
|
resp = buildResp(req, "interrupted", err)
|
|
|
|
|
+ goto retp
|
|
|
// Call-4:客户端安全退出, 释放本地的执行器
|
|
// Call-4:客户端安全退出, 释放本地的执行器
|
|
|
case "executor.close":
|
|
case "executor.close":
|
|
|
- if ce.state == execRunning {
|
|
|
|
|
- ce.mu.Unlock()
|
|
|
|
|
- resp = jsonrpc2.BuildError(req, -32002, "executor busy, interrupt first")
|
|
|
|
|
- goto retp
|
|
|
|
|
- }
|
|
|
|
|
- ce.mu.Unlock()
|
|
|
|
|
|
|
+ err := ce.handleClose()
|
|
|
|
|
|
|
|
c.executorMapMu.Lock()
|
|
c.executorMapMu.Lock()
|
|
|
delete(c.executorMap, clientID)
|
|
delete(c.executorMap, clientID)
|
|
|
c.executorMapMu.Unlock()
|
|
c.executorMapMu.Unlock()
|
|
|
|
|
|
|
|
- resp = buildResp(req, "bye", nil)
|
|
|
|
|
|
|
+ resp = buildResp(req, "closed", err)
|
|
|
goto retp
|
|
goto retp
|
|
|
// Call-?:无效, 远端调用了还不支持的-方法
|
|
// Call-?:无效, 远端调用了还不支持的-方法
|
|
|
default:
|
|
default:
|
|
|
resp = jsonrpc2.BuildError(req, jsonrpc2.ErrMethodNotFound, "")
|
|
resp = jsonrpc2.BuildError(req, jsonrpc2.ErrMethodNotFound, "")
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- ce.mu.Unlock()
|
|
|
|
|
-
|
|
|
|
|
retp:
|
|
retp:
|
|
|
text, err := resp.String()
|
|
text, err := resp.String()
|
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -354,11 +372,36 @@ func (c *MQTTCoupler) startExecutorReaper(interval, timeout time.Duration) {
|
|
|
idle := (ce.state == execIdle)
|
|
idle := (ce.state == execIdle)
|
|
|
ce.mu.Unlock()
|
|
ce.mu.Unlock()
|
|
|
|
|
|
|
|
- if expired && idle {
|
|
|
|
|
|
|
+ if expired && idle { // 超时且执行器状态空闲, 回收该执行器
|
|
|
|
|
+ ce.handleClose()
|
|
|
delete(c.executorMap, id)
|
|
delete(c.executorMap, id)
|
|
|
- }
|
|
|
|
|
- } // end for
|
|
|
|
|
|
|
+ } // end if
|
|
|
|
|
+ } // end for2
|
|
|
c.executorMapMu.Unlock()
|
|
c.executorMapMu.Unlock()
|
|
|
} // end select
|
|
} // end select
|
|
|
- } // end for
|
|
|
|
|
|
|
+ } ////// end for1
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func (ce *clientExecutor) handleClose() error {
|
|
|
|
|
+ needInterrupt := false
|
|
|
|
|
+
|
|
|
|
|
+ ce.mu.Lock()
|
|
|
|
|
+ switch ce.state {
|
|
|
|
|
+ case execIdle:
|
|
|
|
|
+ ce.state = execClosing
|
|
|
|
|
+ case execRunning:
|
|
|
|
|
+ ce.state = execClosing
|
|
|
|
|
+ needInterrupt = true
|
|
|
|
|
+ case execClosing:
|
|
|
|
|
+ ce.mu.Unlock()
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ ce.mu.Unlock()
|
|
|
|
|
+
|
|
|
|
|
+ var err error
|
|
|
|
|
+ if needInterrupt {
|
|
|
|
|
+ err = ce.executor.Interrupt()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|