mirror of
https://shylinux.com/x/icebergs
synced 2025-05-03 12:07:00 +08:00
opt ssh
This commit is contained in:
parent
381e3f3a24
commit
c01b3063da
8
base/mdb/mdb.shy
Normal file
8
base/mdb/mdb.shy
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
chapter "mdb"
|
||||||
|
|
||||||
|
field "搜索" mdb.search
|
||||||
|
field "引擎" mdb.engine
|
||||||
|
|
||||||
|
field "插件" mdb.plugin
|
||||||
|
field "渲染" mdb.render
|
||||||
|
|
5
base/nfs/nfs.shy
Normal file
5
base/nfs/nfs.shy
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
chapter "nfs"
|
||||||
|
|
||||||
|
field "目录" nfs.dir
|
||||||
|
field "文件" nfs.file
|
||||||
|
|
94
base/ssh/connect.go
Normal file
94
base/ssh/connect.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
ice "github.com/shylinux/icebergs"
|
||||||
|
"github.com/shylinux/icebergs/base/aaa"
|
||||||
|
"github.com/shylinux/icebergs/base/mdb"
|
||||||
|
"github.com/shylinux/icebergs/base/nfs"
|
||||||
|
"github.com/shylinux/icebergs/base/tcp"
|
||||||
|
kit "github.com/shylinux/toolkits"
|
||||||
|
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func _ssh_conn(m *ice.Message, conn net.Conn, username, host, port string) (*ssh.Client, error) {
|
||||||
|
methods := []ssh.AuthMethod{}
|
||||||
|
if key, e := ssh.ParsePrivateKey([]byte(m.Cmdx(nfs.CAT, path.Join(os.Getenv("HOME"), m.Conf(PUBLIC, "meta.private"))))); !m.Warn(e != nil) {
|
||||||
|
methods = append(methods, ssh.PublicKeys(key))
|
||||||
|
} else {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
|
||||||
|
c, chans, reqs, err := ssh.NewClientConn(conn, host+":"+port, &ssh.ClientConfig{User: username, Auth: methods,
|
||||||
|
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||||
|
m.Logs(CONNECT, "hostname", hostname, aaa.HOSTPORT, remote.String())
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ssh.NewClient(c, chans, reqs), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONNECT = "connect"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Index.Merge(&ice.Context{
|
||||||
|
Configs: map[string]*ice.Config{
|
||||||
|
CONNECT: {Name: CONNECT, Help: "连接", Value: kit.Data()},
|
||||||
|
},
|
||||||
|
Commands: map[string]*ice.Command{
|
||||||
|
CONNECT: {Name: "connect hash auto 添加 清理", Help: "连接", Action: map[string]*ice.Action{
|
||||||
|
tcp.DIAL: {Name: "dial username=shy host=shylinux.com port=22", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
|
||||||
|
m.Option(tcp.DIAL_CB, func(c net.Conn) {
|
||||||
|
client, e := _ssh_conn(m, c, kit.Select("shy", m.Option(aaa.USERNAME)), kit.Select(m.Option(tcp.HOST), "shylinux.com"), kit.Select("22", m.Option(tcp.PORT)))
|
||||||
|
m.Assert(e)
|
||||||
|
|
||||||
|
h := m.Rich(CONNECT, "", kit.Dict(
|
||||||
|
aaa.USERNAME, m.Option(aaa.USERNAME),
|
||||||
|
tcp.HOST, m.Option(tcp.HOST),
|
||||||
|
tcp.PORT, m.Option(tcp.PORT),
|
||||||
|
kit.MDB_STATUS, tcp.OPEN,
|
||||||
|
CONNECT, client,
|
||||||
|
))
|
||||||
|
m.Cmd(CONNECT, SESSION, kit.MDB_HASH, h)
|
||||||
|
})
|
||||||
|
|
||||||
|
m.Cmds(tcp.CLIENT, tcp.DIAL, arg)
|
||||||
|
}},
|
||||||
|
SESSION: {Name: "session hash", Help: "会话", Hand: func(m *ice.Message, arg ...string) {
|
||||||
|
m.Richs(CONNECT, "", m.Option(kit.MDB_HASH), func(key string, value map[string]interface{}) {
|
||||||
|
client, ok := value[CONNECT].(*ssh.Client)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h := m.Rich(SESSION, "", kit.Data(
|
||||||
|
kit.MDB_STATUS, tcp.OPEN,
|
||||||
|
CONNECT, key,
|
||||||
|
))
|
||||||
|
|
||||||
|
if session, e := _ssh_sess(m, h, client); m.Assert(e) {
|
||||||
|
session.Shell()
|
||||||
|
session.Wait()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}},
|
||||||
|
|
||||||
|
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
|
||||||
|
m.Cmdy(mdb.PRUNES, CONNECT, "", mdb.HASH, kit.MDB_STATUS, tcp.CLOSE)
|
||||||
|
}},
|
||||||
|
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||||
|
m.Option(mdb.FIELDS, "time,hash,status,username,host,port")
|
||||||
|
m.Cmdy(mdb.SELECT, CONNECT, "", mdb.HASH, kit.MDB_HASH, arg)
|
||||||
|
m.PushAction("会话")
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
}
|
357
base/ssh/script.go
Normal file
357
base/ssh/script.go
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
ice "github.com/shylinux/icebergs"
|
||||||
|
"github.com/shylinux/icebergs/base/aaa"
|
||||||
|
"github.com/shylinux/icebergs/base/cli"
|
||||||
|
"github.com/shylinux/icebergs/base/mdb"
|
||||||
|
kit "github.com/shylinux/toolkits"
|
||||||
|
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Render(msg *ice.Message, cmd string, args ...interface{}) {
|
||||||
|
defer func() { msg.Log_EXPORT(mdb.RENDER, cmd, kit.MDB_TEXT, args) }()
|
||||||
|
|
||||||
|
switch arg := kit.Simple(args...); cmd {
|
||||||
|
case ice.RENDER_VOID:
|
||||||
|
case ice.RENDER_RESULT:
|
||||||
|
fmt.Fprintf(msg.O, msg.Result())
|
||||||
|
|
||||||
|
case ice.RENDER_QRCODE:
|
||||||
|
fmt.Fprintf(msg.O, msg.Cmdx(cli.PYTHON, "qrcode", kit.Format(args[0], args[1:]...)))
|
||||||
|
|
||||||
|
case ice.RENDER_DOWNLOAD:
|
||||||
|
if f, e := os.Open(arg[0]); e == nil {
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
io.Copy(msg.O, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// 转换结果
|
||||||
|
res := msg.Result()
|
||||||
|
if res == "" {
|
||||||
|
res = msg.Table().Result()
|
||||||
|
}
|
||||||
|
args = append(args, "length:", len(res))
|
||||||
|
|
||||||
|
// 输出结果
|
||||||
|
if fmt.Fprintf(msg.O, res); !strings.HasSuffix(res, "\n") {
|
||||||
|
fmt.Fprintf(msg.O, "\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func Script(m *ice.Message, name string) io.Reader {
|
||||||
|
if b, ok := ice.BinPack[name]; ok {
|
||||||
|
m.Debug("binpack %v %v", len(b), name)
|
||||||
|
return bytes.NewReader(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(m.Option("_script"), "/") {
|
||||||
|
name = path.Join(path.Dir(m.Option("_script")), name)
|
||||||
|
}
|
||||||
|
m.Option("_script", name)
|
||||||
|
|
||||||
|
if s, e := os.Open(name); e == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
switch strings.Split(name, "/")[0] {
|
||||||
|
case "etc", "var":
|
||||||
|
m.Warn(true, ice.ErrNotFound)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg := m.Cmd("web.spide", "dev", "GET", path.Join("/share/local/", name)); msg.Result(0) != ice.ErrWarn {
|
||||||
|
bio := bytes.NewBuffer([]byte(msg.Result()))
|
||||||
|
return bio
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(name, "usr") {
|
||||||
|
ls := strings.Split(name, "/")
|
||||||
|
m.Cmd("web.code.git.repos", ls[1], "usr/"+ls[1])
|
||||||
|
if s, e := os.Open(name); e == nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Frame struct {
|
||||||
|
source string
|
||||||
|
target *ice.Context
|
||||||
|
stdout io.Writer
|
||||||
|
|
||||||
|
count int
|
||||||
|
ps1 []string
|
||||||
|
ps2 []string
|
||||||
|
|
||||||
|
exit bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Frame) prompt(m *ice.Message, list ...string) *Frame {
|
||||||
|
if f.source != STDIO {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
if len(list) == 0 {
|
||||||
|
list = append(list, f.ps1...)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(f.stdout, "\r")
|
||||||
|
for _, v := range list {
|
||||||
|
switch v {
|
||||||
|
case "count":
|
||||||
|
fmt.Fprintf(f.stdout, "%d", f.count+1)
|
||||||
|
case "time":
|
||||||
|
fmt.Fprintf(f.stdout, time.Now().Format("15:04:05"))
|
||||||
|
case "target":
|
||||||
|
fmt.Fprintf(f.stdout, f.target.Name)
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(f.stdout, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
func (f *Frame) printf(m *ice.Message, res string, arg ...interface{}) *Frame {
|
||||||
|
if len(arg) > 0 {
|
||||||
|
fmt.Fprintf(f.stdout, res, arg...)
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(f.stdout, res)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
func (f *Frame) option(m *ice.Message, ls []string) []string {
|
||||||
|
ln := []string{}
|
||||||
|
m.Option("cache.limit", 10)
|
||||||
|
for i := 0; i < len(ls); i++ {
|
||||||
|
if ls[i] == "--" {
|
||||||
|
ln = append(ln, ls[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(ls[i], "-") {
|
||||||
|
for j := i; j < len(ls); j++ {
|
||||||
|
if j == len(ls)-1 || strings.HasPrefix(ls[j+1], "-") {
|
||||||
|
if i < j {
|
||||||
|
m.Option(ls[i][1:], ls[i+1:j+1])
|
||||||
|
} else {
|
||||||
|
m.Option(ls[i][1:], "true")
|
||||||
|
}
|
||||||
|
i = j
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ln = append(ln, ls[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ln
|
||||||
|
}
|
||||||
|
func (f *Frame) change(m *ice.Message, ls []string) []string {
|
||||||
|
if len(ls) == 1 && ls[0] == "~" {
|
||||||
|
// 模块列表
|
||||||
|
ls = []string{"context"}
|
||||||
|
} else if len(ls) > 0 && strings.HasPrefix(ls[0], "~") {
|
||||||
|
// 切换模块
|
||||||
|
target := ls[0][1:]
|
||||||
|
if ls = ls[1:]; len(target) == 0 && len(ls) > 0 {
|
||||||
|
target, ls = ls[0], ls[1:]
|
||||||
|
}
|
||||||
|
if target == "~" {
|
||||||
|
target = ""
|
||||||
|
}
|
||||||
|
m.Spawn(f.target).Search(target+".", func(p *ice.Context, s *ice.Context, key string) {
|
||||||
|
m.Info("choice: %s", s.Name)
|
||||||
|
f.target = s
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ls
|
||||||
|
}
|
||||||
|
func (f *Frame) alias(m *ice.Message, ls []string) []string {
|
||||||
|
if alias, ok := m.Optionv(ice.MSG_ALIAS).(map[string]interface{}); ok {
|
||||||
|
if len(ls) > 0 {
|
||||||
|
if a := kit.Simple(alias[ls[0]]); len(a) > 0 {
|
||||||
|
ls = append(append([]string{}, a...), ls[1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ls
|
||||||
|
}
|
||||||
|
func (f *Frame) parse(m *ice.Message, line string) string {
|
||||||
|
if strings.HasPrefix(line, "<") {
|
||||||
|
fmt.Fprintf(m.O, line)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, one := range kit.Split(line, ";", ";", ";") {
|
||||||
|
m.Log_IMPORT("stdin", one, "length", len(one))
|
||||||
|
|
||||||
|
async, one := false, strings.TrimSpace(one)
|
||||||
|
if strings.TrimSuffix(one, "&") != one {
|
||||||
|
async, one = true, strings.TrimSuffix(one, "&")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := m.Spawns(f.target)
|
||||||
|
msg.Option("_cmd", one)
|
||||||
|
|
||||||
|
ls := kit.Split(one)
|
||||||
|
ls = f.alias(msg, ls)
|
||||||
|
ls = f.change(msg, ls)
|
||||||
|
ls = f.option(msg, ls)
|
||||||
|
if len(ls) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if async {
|
||||||
|
msg.Gos(msg, func(msg *ice.Message) { msg.Cmd(ls[0], ls[1:]) })
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
msg.Cmdy(ls[0], ls[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(msg.Result(), ice.ErrWarn) && m.Option("render") == "raw" {
|
||||||
|
fmt.Fprintf(msg.O, line)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染引擎
|
||||||
|
_args, _ := msg.Optionv(ice.MSG_ARGS).([]interface{})
|
||||||
|
Render(msg, msg.Option(ice.MSG_OUTPUT), _args...)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
func (f *Frame) scan(m *ice.Message, line string, r io.Reader) *Frame {
|
||||||
|
m.Option("ssh.return", func() { f.exit = true })
|
||||||
|
f.ps1 = kit.Simple(m.Confv("prompt", "meta.PS1"))
|
||||||
|
f.ps2 = kit.Simple(m.Confv("prompt", "meta.PS2"))
|
||||||
|
ps := f.ps1
|
||||||
|
|
||||||
|
m.I, m.O = r, f.stdout
|
||||||
|
bio := bufio.NewScanner(r)
|
||||||
|
for f.prompt(m, ps...); bio.Scan() && !f.exit; f.prompt(m, ps...) {
|
||||||
|
if len(bio.Text()) == 0 {
|
||||||
|
continue // 空行
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(bio.Text(), "\\") {
|
||||||
|
line += bio.Text()[:len(bio.Text())-1]
|
||||||
|
ps = f.ps2
|
||||||
|
continue // 续行
|
||||||
|
}
|
||||||
|
if line += bio.Text(); strings.Count(line, "`")%2 == 1 {
|
||||||
|
line += "\n"
|
||||||
|
ps = f.ps2
|
||||||
|
continue // 多行
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(strings.TrimSpace(line), "#") {
|
||||||
|
line = ""
|
||||||
|
continue // 注释
|
||||||
|
}
|
||||||
|
// if line = f.history(m, line); line == "" {
|
||||||
|
// // 历史命令
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
if ps = f.ps1; f.stdout == os.Stdout {
|
||||||
|
// 清空格式
|
||||||
|
f.printf(m, "\033[0m")
|
||||||
|
}
|
||||||
|
line = f.parse(m, line)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Frame) Begin(m *ice.Message, arg ...string) ice.Server {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
func (f *Frame) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server {
|
||||||
|
return &Frame{}
|
||||||
|
}
|
||||||
|
func (f *Frame) Start(m *ice.Message, arg ...string) bool {
|
||||||
|
f.source, f.target = kit.Select(STDIO, arg, 0), m.Target()
|
||||||
|
|
||||||
|
var r io.Reader
|
||||||
|
switch m.Cap(ice.CTX_STREAM, f.source) {
|
||||||
|
case STDIO: // 终端交互
|
||||||
|
r, f.stdout = os.Stdin, os.Stdout
|
||||||
|
|
||||||
|
m.Option("_option", ice.MSG_USERNAME)
|
||||||
|
m.Option(ice.MSG_USERNAME, cli.UserName)
|
||||||
|
m.Option(ice.MSG_USERROLE, aaa.ROOT)
|
||||||
|
m.Option(ice.MSG_USERZONE, "boot")
|
||||||
|
aaa.UserRoot(m)
|
||||||
|
default:
|
||||||
|
f.target = m.Source()
|
||||||
|
|
||||||
|
if strings.HasPrefix(f.source, "/dev") {
|
||||||
|
r, f.stdout = m.I, m.O
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, 4096))
|
||||||
|
defer func() { m.Echo(buf.String()) }()
|
||||||
|
|
||||||
|
if s := Script(m, f.source); s != nil {
|
||||||
|
r, f.stdout = s, buf
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
f.scan(m, "", r)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
func (f *Frame) Close(m *ice.Message, arg ...string) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
STDIO = "stdio"
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
SOURCE = "source"
|
||||||
|
TARGET = "target"
|
||||||
|
PROMPT = "prompt"
|
||||||
|
RETURN = "return"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Index.Merge(&ice.Context{
|
||||||
|
Configs: map[string]*ice.Config{
|
||||||
|
SOURCE: {Name: SOURCE, Help: "加载脚本", Value: kit.Data()},
|
||||||
|
PROMPT: {Name: PROMPT, Help: "命令提示", Value: kit.Data(
|
||||||
|
"PS1", []interface{}{"\033[33;44m", "count", "[", "time", "]", "\033[5m", "target", "\033[0m", "\033[44m", ">", "\033[0m ", "\033[?25h", "\033[32m"},
|
||||||
|
"PS2", []interface{}{"count", " ", "target", "> "},
|
||||||
|
)},
|
||||||
|
},
|
||||||
|
Commands: map[string]*ice.Command{
|
||||||
|
SOURCE: {Name: "source file", Help: "脚本解析", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||||
|
m.Starts(strings.Replace(arg[0], ".", "_", -1), arg[0], arg[0:]...)
|
||||||
|
}},
|
||||||
|
TARGET: {Name: "target name", Help: "当前模块", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||||
|
m.Search(arg[0], func(p *ice.Context, s *ice.Context, key string) {
|
||||||
|
f := m.Target().Server().(*Frame)
|
||||||
|
f.target = s
|
||||||
|
f.prompt(m)
|
||||||
|
})
|
||||||
|
}},
|
||||||
|
PROMPT: {Name: "prompt arg...", Help: "命令提示", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||||
|
f := m.Target().Server().(*Frame)
|
||||||
|
f.ps1 = arg
|
||||||
|
f.prompt(m)
|
||||||
|
}},
|
||||||
|
RETURN: {Name: "return", Help: "结束脚本", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||||
|
switch cb := m.Optionv("ssh.return").(type) {
|
||||||
|
case func():
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
}
|
@ -51,43 +51,6 @@ func _ssh_close(m *ice.Message, c net.Conn, channel ssh.Channel) {
|
|||||||
defer channel.Close()
|
defer channel.Close()
|
||||||
channel.Write([]byte(m.Conf(PUBLIC, "meta.goodbye")))
|
channel.Write([]byte(m.Conf(PUBLIC, "meta.goodbye")))
|
||||||
}
|
}
|
||||||
func _ssh_trace(m *ice.Message, meta map[string]string, input io.Reader, output io.Writer, display io.Writer) {
|
|
||||||
m.Gos(m, func(m *ice.Message) {
|
|
||||||
i, buf := 0, make([]byte, 1024)
|
|
||||||
for {
|
|
||||||
n, e := input.Read(buf[i:])
|
|
||||||
if e != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
switch buf[i] {
|
|
||||||
case '\r', '\n':
|
|
||||||
cmd := strings.TrimSpace(string(buf[:i]))
|
|
||||||
m.Log_IMPORT("hostname", meta["hostname"], "username", meta["username"], "buf", buf[:i+n])
|
|
||||||
m.Conf(CONNECT, kit.Keys(kit.MDB_HASH, meta[CONNECT], "duration"), m.Format("cost"))
|
|
||||||
m.Conf(SESSION, kit.Keys(kit.MDB_HASH, meta[SESSION], "cmd"), cmd)
|
|
||||||
|
|
||||||
msg := m.Cmd(cmd).Table()
|
|
||||||
res := strings.TrimSpace(strings.ReplaceAll(msg.Result(), "\n", "\r\n"))
|
|
||||||
if len(res) > 0 {
|
|
||||||
|
|
||||||
fmt.Fprintf(display, "\r\n")
|
|
||||||
fmt.Fprintf(display, res)
|
|
||||||
fmt.Fprintf(display, "\r\n")
|
|
||||||
output.Write([]byte{21, 10})
|
|
||||||
} else {
|
|
||||||
output.Write(buf[i : i+n])
|
|
||||||
}
|
|
||||||
i = 0
|
|
||||||
default:
|
|
||||||
output.Write(buf[i : i+n])
|
|
||||||
|
|
||||||
if i += n; i >= 1024 {
|
|
||||||
i = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
func _ssh_watch(m *ice.Message, meta map[string]string, input io.Reader, output io.Writer, display io.Writer) {
|
func _ssh_watch(m *ice.Message, meta map[string]string, input io.Reader, output io.Writer, display io.Writer) {
|
||||||
m.Gos(m, func(m *ice.Message) {
|
m.Gos(m, func(m *ice.Message) {
|
||||||
r, w := io.Pipe()
|
r, w := io.Pipe()
|
||||||
@ -125,13 +88,13 @@ func _ssh_handle(m *ice.Message, meta map[string]string, c net.Conn, channel ssh
|
|||||||
shell := kit.Select("bash", os.Getenv("SHELL"))
|
shell := kit.Select("bash", os.Getenv("SHELL"))
|
||||||
list := []string{"PATH=" + os.Getenv("PATH")}
|
list := []string{"PATH=" + os.Getenv("PATH")}
|
||||||
|
|
||||||
tty, f, err := pty.Open()
|
pty, tty, err := pty.Open()
|
||||||
if m.Warn(err != nil, err) {
|
if m.Warn(err != nil, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer tty.Close()
|
||||||
|
|
||||||
h := m.Cmdx(mdb.INSERT, m.Prefix(SESSION), "", mdb.HASH, aaa.HOSTPORT, c.RemoteAddr().String(), kit.MDB_STATUS, "open", "tty", tty.Name())
|
h := m.Cmdx(mdb.INSERT, m.Prefix(SESSION), "", mdb.HASH, aaa.HOSTPORT, c.RemoteAddr().String(), kit.MDB_STATUS, "open", "pty", pty.Name())
|
||||||
m.Richs(SESSION, "", h, func(key string, value map[string]interface{}) { value["channel"] = channel })
|
m.Richs(SESSION, "", h, func(key string, value map[string]interface{}) { value["channel"] = channel })
|
||||||
meta[SESSION] = h
|
meta[SESSION] = h
|
||||||
|
|
||||||
@ -142,11 +105,11 @@ func _ssh_handle(m *ice.Message, meta map[string]string, c net.Conn, channel ssh
|
|||||||
case "pty-req":
|
case "pty-req":
|
||||||
termLen := request.Payload[3]
|
termLen := request.Payload[3]
|
||||||
termEnv := string(request.Payload[4 : termLen+4])
|
termEnv := string(request.Payload[4 : termLen+4])
|
||||||
_ssh_size(tty.Fd(), request.Payload[termLen+4:])
|
_ssh_size(pty.Fd(), request.Payload[termLen+4:])
|
||||||
list = append(list, "TERM="+termEnv)
|
list = append(list, "TERM="+termEnv)
|
||||||
|
|
||||||
case "window-change":
|
case "window-change":
|
||||||
_ssh_size(tty.Fd(), request.Payload)
|
_ssh_size(pty.Fd(), request.Payload)
|
||||||
|
|
||||||
case "env":
|
case "env":
|
||||||
var env struct {
|
var env struct {
|
||||||
@ -163,47 +126,25 @@ func _ssh_handle(m *ice.Message, meta map[string]string, c net.Conn, channel ssh
|
|||||||
channel, func() { channel.Close() })
|
channel, func() { channel.Close() })
|
||||||
case "shell":
|
case "shell":
|
||||||
if meta["username"] == "ssh" {
|
if meta["username"] == "ssh" {
|
||||||
m.I, m.O = f, f
|
m.I, m.O = tty, tty
|
||||||
m.Render(ice.RENDER_VOID)
|
m.Render(ice.RENDER_VOID)
|
||||||
m.Gos(m, func(m *ice.Message) {
|
m.Gos(m, func(m *ice.Message) {
|
||||||
m.Cmdy(SOURCE, tty.Name())
|
m.Cmdy(SOURCE, pty.Name())
|
||||||
_ssh_close(m, c, channel)
|
_ssh_close(m, c, channel)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
_ssh_exec(m, shell, nil, list, f, func() {
|
_ssh_exec(m, shell, nil, list, tty, func() {
|
||||||
defer m.Cmd(mdb.MODIFY, m.Prefix(SESSION), "", mdb.HASH, kit.MDB_HASH, h, kit.MDB_STATUS, "close")
|
defer m.Cmd(mdb.MODIFY, m.Prefix(SESSION), "", mdb.HASH, kit.MDB_HASH, h, kit.MDB_STATUS, "close")
|
||||||
_ssh_close(m, c, channel)
|
_ssh_close(m, c, channel)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Gos(m, func(m *ice.Message) { io.Copy(channel, tty) })
|
m.Gos(m, func(m *ice.Message) { io.Copy(channel, pty) })
|
||||||
_ssh_watch(m, meta, channel, tty, channel)
|
_ssh_watch(m, meta, channel, pty, channel)
|
||||||
// _ssh_trace(m, meta, channel, tty, channel)
|
|
||||||
}
|
}
|
||||||
request.Reply(true, nil)
|
request.Reply(true, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func _ssh_accept(m *ice.Message, c net.Conn) {
|
|
||||||
sc, sessions, req, err := ssh.NewServerConn(c, _ssh_config(m))
|
|
||||||
if m.Warn(err != nil, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
m.Gos(m, func(m *ice.Message) { ssh.DiscardRequests(req) })
|
|
||||||
|
|
||||||
for session := range sessions {
|
|
||||||
channel, requests, err := session.Accept()
|
|
||||||
if m.Warn(err != nil, err) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
func(channel ssh.Channel, requests <-chan *ssh.Request) {
|
|
||||||
m.Gos(m, func(m *ice.Message) {
|
|
||||||
_ssh_handle(m, sc.Permissions.Extensions, c, channel, requests)
|
|
||||||
})
|
|
||||||
}(channel, requests)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func _ssh_config(m *ice.Message) *ssh.ServerConfig {
|
func _ssh_config(m *ice.Message) *ssh.ServerConfig {
|
||||||
config := &ssh.ServerConfig{
|
config := &ssh.ServerConfig{
|
||||||
BannerCallback: func(conn ssh.ConnMetadata) string {
|
BannerCallback: func(conn ssh.ConnMetadata) string {
|
||||||
@ -250,28 +191,10 @@ func _ssh_config(m *ice.Message) *ssh.ServerConfig {
|
|||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
func _ssh_dial(m *ice.Message, username, hostport string) (*ssh.Client, error) {
|
|
||||||
methods := []ssh.AuthMethod{}
|
|
||||||
if key, e := ssh.ParsePrivateKey([]byte(m.Cmdx(nfs.CAT, path.Join(os.Getenv("HOME"), m.Conf(PUBLIC, "meta.private"))))); !m.Warn(e != nil) {
|
|
||||||
methods = append(methods, ssh.PublicKeys(key))
|
|
||||||
} else {
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
|
|
||||||
connect, e := ssh.Dial("tcp", hostport, &ssh.ClientConfig{User: username, Auth: methods,
|
|
||||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
|
||||||
m.Logs(CONNECT, "hostname", hostname, aaa.HOSTPORT, remote.String())
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return connect, e
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ADDRESS = "address"
|
ADDRESS = "address"
|
||||||
CONNECT = "connect"
|
|
||||||
CHANNEL = "channel"
|
CHANNEL = "channel"
|
||||||
SESSION = "session"
|
|
||||||
REQUEST = "request"
|
REQUEST = "request"
|
||||||
COMMAND = "command"
|
COMMAND = "command"
|
||||||
)
|
)
|
||||||
@ -295,19 +218,9 @@ func init() {
|
|||||||
LISTEN: {Name: LISTEN, Help: "服务", Value: kit.Data(kit.MDB_SHORT, aaa.HOSTPORT,
|
LISTEN: {Name: LISTEN, Help: "服务", Value: kit.Data(kit.MDB_SHORT, aaa.HOSTPORT,
|
||||||
kit.MDB_FIELD, "time,hash,hostport,status",
|
kit.MDB_FIELD, "time,hash,hostport,status",
|
||||||
)},
|
)},
|
||||||
CONNECT: {Name: CONNECT, Help: "连接", Value: kit.Data(
|
|
||||||
kit.MDB_FIELD, "time,hash,hostport,status,duration,close_time,hostname,username",
|
|
||||||
)},
|
|
||||||
SESSION: {Name: SESSION, Help: "会话", Value: kit.Data(
|
|
||||||
kit.MDB_FIELD, "time,hash,hostport,status,tty,cmd",
|
|
||||||
)},
|
|
||||||
COMMAND: {Name: COMMAND, Help: "命令", Value: kit.Data(
|
COMMAND: {Name: COMMAND, Help: "命令", Value: kit.Data(
|
||||||
kit.MDB_FIELD, "time,id,username,hostname,cmd",
|
kit.MDB_FIELD, "time,id,username,hostname,cmd",
|
||||||
)},
|
)},
|
||||||
|
|
||||||
DIAL: {Name: DIAL, Help: "连接", Value: kit.Data(
|
|
||||||
kit.MDB_FIELD, "time,hash,hostport,username",
|
|
||||||
)},
|
|
||||||
},
|
},
|
||||||
Commands: map[string]*ice.Command{
|
Commands: map[string]*ice.Command{
|
||||||
PUBLIC: {Name: "public hash=auto auto 添加 导出 导入", Help: "公钥", Action: map[string]*ice.Action{
|
PUBLIC: {Name: "public hash=auto auto 添加 导出 导入", Help: "公钥", Action: map[string]*ice.Action{
|
||||||
@ -360,26 +273,6 @@ func init() {
|
|||||||
}
|
}
|
||||||
m.Cmdy(mdb.SELECT, m.Prefix(LISTEN), "", mdb.HASH, kit.MDB_HASH, arg)
|
m.Cmdy(mdb.SELECT, m.Prefix(LISTEN), "", mdb.HASH, kit.MDB_HASH, arg)
|
||||||
}},
|
}},
|
||||||
CONNECT: {Name: "connect hash=auto auto 清理", Help: "连接", Action: map[string]*ice.Action{
|
|
||||||
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
|
|
||||||
m.Cmdy(mdb.PRUNES, m.Prefix(CONNECT), "", mdb.HASH, kit.MDB_STATUS, "close")
|
|
||||||
}},
|
|
||||||
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
|
||||||
if m.Option(mdb.FIELDS, m.Conf(CONNECT, kit.META_FIELD)); len(arg) > 0 {
|
|
||||||
m.Option(mdb.FIELDS, mdb.DETAIL)
|
|
||||||
}
|
|
||||||
m.Cmdy(mdb.SELECT, m.Prefix(CONNECT), "", mdb.HASH, kit.MDB_HASH, arg)
|
|
||||||
}},
|
|
||||||
SESSION: {Name: "session hash auto 清理", Help: "会话", Action: map[string]*ice.Action{
|
|
||||||
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
|
|
||||||
m.Cmdy(mdb.PRUNES, m.Prefix(SESSION), "", mdb.HASH, kit.MDB_STATUS, "close")
|
|
||||||
}},
|
|
||||||
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
|
||||||
if m.Option(mdb.FIELDS, m.Conf(SESSION, kit.META_FIELD)); len(arg) > 0 {
|
|
||||||
m.Option(mdb.FIELDS, mdb.DETAIL)
|
|
||||||
}
|
|
||||||
m.Cmdy(mdb.SELECT, m.Prefix(SESSION), "", mdb.HASH, kit.MDB_HASH, arg)
|
|
||||||
}},
|
|
||||||
COMMAND: {Name: "command id=auto auto", Help: "命令", Action: map[string]*ice.Action{
|
COMMAND: {Name: "command id=auto auto", Help: "命令", Action: map[string]*ice.Action{
|
||||||
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
|
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
|
||||||
m.Cmdy(mdb.PRUNES, m.Prefix(CONNECT), "", mdb.HASH, kit.MDB_STATUS, "close")
|
m.Cmdy(mdb.PRUNES, m.Prefix(CONNECT), "", mdb.HASH, kit.MDB_STATUS, "close")
|
||||||
@ -390,61 +283,6 @@ func init() {
|
|||||||
}
|
}
|
||||||
m.Cmdy(mdb.SELECT, m.Prefix(COMMAND), "", mdb.LIST, kit.MDB_ID, arg)
|
m.Cmdy(mdb.SELECT, m.Prefix(COMMAND), "", mdb.LIST, kit.MDB_ID, arg)
|
||||||
}},
|
}},
|
||||||
|
|
||||||
DIAL: {Name: "dial hash=auto auto 添加 导出 导入 cmd:textarea=pwd", Help: "连接", Action: map[string]*ice.Action{
|
|
||||||
mdb.CREATE: {Name: "create username=shy hostname=shylinux.com port=22", Help: "添加", Hand: func(m *ice.Message, arg ...string) {
|
|
||||||
if connect, e := _ssh_dial(m, m.Option(aaa.USERNAME), m.Option(aaa.HOSTPORT, m.Option("hostname")+":"+m.Option("port"))); m.Assert(e) {
|
|
||||||
h := m.Rich(DIAL, "", kit.Dict(aaa.USERNAME, m.Option(aaa.USERNAME), aaa.HOSTPORT, m.Option(aaa.HOSTPORT), CONNECT, connect))
|
|
||||||
m.Echo(h)
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
mdb.DELETE: {Name: "delete", Help: "删除", Hand: func(m *ice.Message, arg ...string) {
|
|
||||||
m.Cmdy(mdb.DELETE, m.Prefix(DIAL), "", mdb.HASH, kit.MDB_HASH, m.Option(kit.MDB_HASH))
|
|
||||||
}},
|
|
||||||
mdb.EXPORT: {Name: "export file=.ssh/known_hosts", Help: "导出", Hand: func(m *ice.Message, arg ...string) {
|
|
||||||
list := []string{}
|
|
||||||
if m.Cmd(mdb.SELECT, m.Prefix(PUBLIC), "", mdb.HASH).Table(func(index int, value map[string]string, head []string) {
|
|
||||||
list = append(list, fmt.Sprintf("%s %s %s", value[kit.MDB_TYPE], value[kit.MDB_TEXT], value[kit.MDB_NAME]))
|
|
||||||
}); len(list) > 0 {
|
|
||||||
m.Cmdy(nfs.SAVE, path.Join(os.Getenv("HOME"), m.Option(kit.MDB_FILE)), strings.Join(list, "\n")+"\n")
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
mdb.IMPORT: {Name: "import file=.ssh/known_hosts", Help: "导入", Hand: func(m *ice.Message, arg ...string) {
|
|
||||||
p := path.Join(os.Getenv("HOME"), m.Option(kit.MDB_FILE))
|
|
||||||
for _, pub := range strings.Split(m.Cmdx(nfs.CAT, p), "\n") {
|
|
||||||
if len(pub) > 10 {
|
|
||||||
m.Cmd(PUBLIC, mdb.CREATE, "publickey", pub)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m.Echo(p)
|
|
||||||
}},
|
|
||||||
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
|
||||||
if len(arg) == 0 || arg[0] == "" {
|
|
||||||
m.Option(mdb.FIELDS, m.Conf(DIAL, kit.META_FIELD))
|
|
||||||
m.Cmdy(mdb.SELECT, m.Prefix(DIAL), "", mdb.HASH)
|
|
||||||
m.PushAction("删除")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
m.Richs(DIAL, "", arg[0], func(key string, value map[string]interface{}) {
|
|
||||||
connect, ok := value[CONNECT].(*ssh.Client)
|
|
||||||
if !ok {
|
|
||||||
if c, e := _ssh_dial(m, kit.Format(value[aaa.USERNAME]), kit.Format(value[aaa.HOSTPORT])); m.Assert(e) {
|
|
||||||
connect, value[CONNECT] = c, c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
session, e := connect.NewSession()
|
|
||||||
m.Assert(e)
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
session.Stdout = &b
|
|
||||||
|
|
||||||
m.Assert(session.Run(arg[1]))
|
|
||||||
m.Echo(b.String())
|
|
||||||
})
|
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
62
base/ssh/service.go
Normal file
62
base/ssh/service.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
ice "github.com/shylinux/icebergs"
|
||||||
|
"github.com/shylinux/icebergs/base/mdb"
|
||||||
|
"github.com/shylinux/icebergs/base/tcp"
|
||||||
|
kit "github.com/shylinux/toolkits"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func _ssh_accept(m *ice.Message, c net.Conn) {
|
||||||
|
sc, sessions, req, err := ssh.NewServerConn(c, _ssh_config(m))
|
||||||
|
if m.Warn(err != nil, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Gos(m, func(m *ice.Message) { ssh.DiscardRequests(req) })
|
||||||
|
|
||||||
|
for session := range sessions {
|
||||||
|
channel, requests, err := session.Accept()
|
||||||
|
if m.Warn(err != nil, err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
func(channel ssh.Channel, requests <-chan *ssh.Request) {
|
||||||
|
m.Gos(m, func(m *ice.Message) {
|
||||||
|
_ssh_handle(m, sc.Permissions.Extensions, c, channel, requests)
|
||||||
|
})
|
||||||
|
}(channel, requests)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SERVICE = "service"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Index.Merge(&ice.Context{
|
||||||
|
Configs: map[string]*ice.Config{
|
||||||
|
SERVICE: {Name: SERVICE, Help: "服务", Value: kit.Data()},
|
||||||
|
},
|
||||||
|
Commands: map[string]*ice.Command{
|
||||||
|
SERVICE: {Name: "service", Help: "服务", Action: map[string]*ice.Action{
|
||||||
|
tcp.LISTEN: {Name: "listen name=tcp port=9030", Help: "监听", Hand: func(m *ice.Message, arg ...string) {
|
||||||
|
m.Option(tcp.LISTEN_CB, func(c net.Conn) {
|
||||||
|
m.Gos(m.Spawn(), func(msg *ice.Message) { _ssh_accept(msg, c) })
|
||||||
|
})
|
||||||
|
m.Gos(m, func(m *ice.Message) {
|
||||||
|
m.Cmdy(tcp.SERVER, tcp.LISTEN, kit.MDB_NAME, "ssh", tcp.PORT, m.Option(tcp.PORT))
|
||||||
|
})
|
||||||
|
}},
|
||||||
|
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||||
|
if m.Option(mdb.FIELDS, m.Conf(LISTEN, kit.META_FIELD)); len(arg) > 0 {
|
||||||
|
m.Option(mdb.FIELDS, mdb.DETAIL)
|
||||||
|
}
|
||||||
|
m.Option(mdb.FIELDS, "time,hash,status,host,port")
|
||||||
|
m.Cmdy(mdb.SELECT, m.Prefix(LISTEN), "", mdb.HASH, kit.MDB_HASH, arg)
|
||||||
|
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
}
|
91
base/ssh/session.go
Normal file
91
base/ssh/session.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
ice "github.com/shylinux/icebergs"
|
||||||
|
"github.com/shylinux/icebergs/base/ctx"
|
||||||
|
"github.com/shylinux/icebergs/base/mdb"
|
||||||
|
"github.com/shylinux/icebergs/base/tcp"
|
||||||
|
kit "github.com/shylinux/toolkits"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
func _ssh_sess(m *ice.Message, h string, client *ssh.Client) (*ssh.Session, error) {
|
||||||
|
session, e := client.NewSession()
|
||||||
|
m.Assert(e)
|
||||||
|
|
||||||
|
out, e := session.StdoutPipe()
|
||||||
|
m.Assert(e)
|
||||||
|
|
||||||
|
in, e := session.StdinPipe()
|
||||||
|
m.Assert(e)
|
||||||
|
|
||||||
|
m.Go(func() {
|
||||||
|
for {
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
n, e := out.Read(buf)
|
||||||
|
if e != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Debug(string(buf[:n]))
|
||||||
|
m.Grow(SESSION, kit.Keys(kit.MDB_HASH, h), kit.Dict(
|
||||||
|
kit.MDB_TYPE, RES, kit.MDB_TEXT, string(buf[:n]),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
m.Richs(SESSION, "", h, func(key string, value map[string]interface{}) {
|
||||||
|
kit.Value(value, "meta.output", out)
|
||||||
|
kit.Value(value, "meta.input", in)
|
||||||
|
})
|
||||||
|
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
CMD = "cmd"
|
||||||
|
ARG = "arg"
|
||||||
|
ENV = "env"
|
||||||
|
RES = "res"
|
||||||
|
)
|
||||||
|
|
||||||
|
const SESSION = "session"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Index.Merge(&ice.Context{
|
||||||
|
Configs: map[string]*ice.Config{
|
||||||
|
SESSION: {Name: SESSION, Help: "会话", Value: kit.Data()},
|
||||||
|
},
|
||||||
|
Commands: map[string]*ice.Command{
|
||||||
|
SESSION: {Name: "session hash id auto 命令 清理", Help: "会话", Action: map[string]*ice.Action{
|
||||||
|
ctx.COMMAND: {Name: "command cmd=pwd", Help: "命令", Hand: func(m *ice.Message, arg ...string) {
|
||||||
|
m.Richs(SESSION, "", m.Option(kit.MDB_HASH), func(key string, value map[string]interface{}) {
|
||||||
|
if w, ok := kit.Value(value, "meta.input").(io.Writer); ok {
|
||||||
|
m.Grow(SESSION, kit.Keys(kit.MDB_HASH, key), kit.Dict(kit.MDB_TYPE, RES, kit.MDB_TEXT, m.Option(CMD)))
|
||||||
|
n, e := w.Write([]byte(m.Option(CMD) + "\n"))
|
||||||
|
m.Debug("%v %v", n, e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
m.Sleep("300ms")
|
||||||
|
m.Cmdy(SESSION, m.Option(kit.MDB_HASH))
|
||||||
|
}},
|
||||||
|
|
||||||
|
mdb.PRUNES: {Name: "prunes", Help: "清理", Hand: func(m *ice.Message, arg ...string) {
|
||||||
|
m.Cmdy(mdb.PRUNES, SESSION, "", mdb.HASH, kit.MDB_STATUS, tcp.CLOSE)
|
||||||
|
}},
|
||||||
|
}, Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||||
|
if len(arg) == 0 {
|
||||||
|
m.Option(mdb.FIELDS, "time,hash,status,count,connect")
|
||||||
|
m.Cmdy(mdb.SELECT, SESSION, "", mdb.HASH, kit.MDB_HASH, arg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Option(mdb.FIELDS, "time,id,type,text")
|
||||||
|
m.Cmdy(mdb.SELECT, SESSION, kit.Keys(kit.MDB_HASH, arg[0]), mdb.LIST, kit.MDB_ID, arg[1:])
|
||||||
|
m.Sort(kit.MDB_ID)
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
}
|
384
base/ssh/ssh.go
384
base/ssh/ssh.go
@ -2,367 +2,39 @@ package ssh
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
ice "github.com/shylinux/icebergs"
|
ice "github.com/shylinux/icebergs"
|
||||||
"github.com/shylinux/icebergs/base/aaa"
|
"github.com/shylinux/icebergs/base/tcp"
|
||||||
"github.com/shylinux/icebergs/base/cli"
|
|
||||||
"github.com/shylinux/icebergs/base/mdb"
|
|
||||||
kit "github.com/shylinux/toolkits"
|
kit "github.com/shylinux/toolkits"
|
||||||
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Render(msg *ice.Message, cmd string, args ...interface{}) {
|
|
||||||
defer func() { msg.Log_EXPORT(mdb.RENDER, cmd, kit.MDB_TEXT, args) }()
|
|
||||||
|
|
||||||
switch arg := kit.Simple(args...); cmd {
|
|
||||||
case ice.RENDER_VOID:
|
|
||||||
case ice.RENDER_RESULT:
|
|
||||||
fmt.Fprintf(msg.O, msg.Result())
|
|
||||||
|
|
||||||
case ice.RENDER_QRCODE:
|
|
||||||
fmt.Fprintf(msg.O, msg.Cmdx(cli.PYTHON, "qrcode", kit.Format(args[0], args[1:]...)))
|
|
||||||
|
|
||||||
case ice.RENDER_DOWNLOAD:
|
|
||||||
if f, e := os.Open(arg[0]); e == nil {
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
io.Copy(msg.O, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// 转换结果
|
|
||||||
res := msg.Result()
|
|
||||||
if res == "" {
|
|
||||||
res = msg.Table().Result()
|
|
||||||
}
|
|
||||||
args = append(args, "length:", len(res))
|
|
||||||
|
|
||||||
// 输出结果
|
|
||||||
if fmt.Fprintf(msg.O, res); !strings.HasSuffix(res, "\n") {
|
|
||||||
fmt.Fprintf(msg.O, "\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func Script(m *ice.Message, name string) io.Reader {
|
|
||||||
if b, ok := ice.BinPack[name]; ok {
|
|
||||||
m.Debug("binpack %v %v", len(b), name)
|
|
||||||
return bytes.NewReader(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Contains(m.Option("_script"), "/") {
|
|
||||||
name = path.Join(path.Dir(m.Option("_script")), name)
|
|
||||||
}
|
|
||||||
m.Option("_script", name)
|
|
||||||
|
|
||||||
if s, e := os.Open(name); e == nil {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
switch strings.Split(name, "/")[0] {
|
|
||||||
case "etc", "var":
|
|
||||||
m.Warn(true, ice.ErrNotFound)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg := m.Cmd("web.spide", "dev", "GET", path.Join("/share/local/", name)); msg.Result(0) != ice.ErrWarn {
|
|
||||||
bio := bytes.NewBuffer([]byte(msg.Result()))
|
|
||||||
return bio
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(name, "usr") {
|
|
||||||
ls := strings.Split(name, "/")
|
|
||||||
m.Cmd("web.code.git.repos", ls[1], "usr/"+ls[1])
|
|
||||||
if s, e := os.Open(name); e == nil {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Frame struct {
|
|
||||||
source string
|
|
||||||
target *ice.Context
|
|
||||||
stdout io.Writer
|
|
||||||
|
|
||||||
count int
|
|
||||||
ps1 []string
|
|
||||||
ps2 []string
|
|
||||||
|
|
||||||
exit bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Frame) prompt(m *ice.Message, list ...string) *Frame {
|
|
||||||
if f.source != STDIO {
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
if len(list) == 0 {
|
|
||||||
list = append(list, f.ps1...)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(f.stdout, "\r")
|
|
||||||
for _, v := range list {
|
|
||||||
switch v {
|
|
||||||
case "count":
|
|
||||||
fmt.Fprintf(f.stdout, "%d", f.count+1)
|
|
||||||
case "time":
|
|
||||||
fmt.Fprintf(f.stdout, time.Now().Format("15:04:05"))
|
|
||||||
case "target":
|
|
||||||
fmt.Fprintf(f.stdout, f.target.Name)
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(f.stdout, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
func (f *Frame) printf(m *ice.Message, res string, arg ...interface{}) *Frame {
|
|
||||||
if len(arg) > 0 {
|
|
||||||
fmt.Fprintf(f.stdout, res, arg...)
|
|
||||||
} else {
|
|
||||||
fmt.Fprint(f.stdout, res)
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
func (f *Frame) option(m *ice.Message, ls []string) []string {
|
|
||||||
ln := []string{}
|
|
||||||
m.Option("cache.limit", 10)
|
|
||||||
for i := 0; i < len(ls); i++ {
|
|
||||||
if ls[i] == "--" {
|
|
||||||
ln = append(ln, ls[i+1:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(ls[i], "-") {
|
|
||||||
for j := i; j < len(ls); j++ {
|
|
||||||
if j == len(ls)-1 || strings.HasPrefix(ls[j+1], "-") {
|
|
||||||
if i < j {
|
|
||||||
m.Option(ls[i][1:], ls[i+1:j+1])
|
|
||||||
} else {
|
|
||||||
m.Option(ls[i][1:], "true")
|
|
||||||
}
|
|
||||||
i = j
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ln = append(ln, ls[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ln
|
|
||||||
}
|
|
||||||
func (f *Frame) change(m *ice.Message, ls []string) []string {
|
|
||||||
if len(ls) == 1 && ls[0] == "~" {
|
|
||||||
// 模块列表
|
|
||||||
ls = []string{"context"}
|
|
||||||
} else if len(ls) > 0 && strings.HasPrefix(ls[0], "~") {
|
|
||||||
// 切换模块
|
|
||||||
target := ls[0][1:]
|
|
||||||
if ls = ls[1:]; len(target) == 0 && len(ls) > 0 {
|
|
||||||
target, ls = ls[0], ls[1:]
|
|
||||||
}
|
|
||||||
if target == "~" {
|
|
||||||
target = ""
|
|
||||||
}
|
|
||||||
m.Spawn(f.target).Search(target+".", func(p *ice.Context, s *ice.Context, key string) {
|
|
||||||
m.Info("choice: %s", s.Name)
|
|
||||||
f.target = s
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return ls
|
|
||||||
}
|
|
||||||
func (f *Frame) alias(m *ice.Message, ls []string) []string {
|
|
||||||
if alias, ok := m.Optionv(ice.MSG_ALIAS).(map[string]interface{}); ok {
|
|
||||||
if len(ls) > 0 {
|
|
||||||
if a := kit.Simple(alias[ls[0]]); len(a) > 0 {
|
|
||||||
ls = append(append([]string{}, a...), ls[1:]...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ls
|
|
||||||
}
|
|
||||||
func (f *Frame) parse(m *ice.Message, line string) string {
|
|
||||||
if strings.HasPrefix(line, "<") {
|
|
||||||
fmt.Fprintf(m.O, line)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, one := range kit.Split(line, ";", ";", ";") {
|
|
||||||
m.Log_IMPORT("stdin", one, "length", len(one))
|
|
||||||
|
|
||||||
async, one := false, strings.TrimSpace(one)
|
|
||||||
if strings.TrimSuffix(one, "&") != one {
|
|
||||||
async, one = true, strings.TrimSuffix(one, "&")
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := m.Spawns(f.target)
|
|
||||||
msg.Option("_cmd", one)
|
|
||||||
|
|
||||||
ls := kit.Split(one)
|
|
||||||
ls = f.alias(msg, ls)
|
|
||||||
ls = f.change(msg, ls)
|
|
||||||
ls = f.option(msg, ls)
|
|
||||||
if len(ls) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if async {
|
|
||||||
msg.Gos(msg, func(msg *ice.Message) { msg.Cmd(ls[0], ls[1:]) })
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
msg.Cmdy(ls[0], ls[1:])
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(msg.Result(), ice.ErrWarn) && m.Option("render") == "raw" {
|
|
||||||
fmt.Fprintf(msg.O, line)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 渲染引擎
|
|
||||||
_args, _ := msg.Optionv(ice.MSG_ARGS).([]interface{})
|
|
||||||
Render(msg, msg.Option(ice.MSG_OUTPUT), _args...)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
func (f *Frame) scan(m *ice.Message, line string, r io.Reader) *Frame {
|
|
||||||
m.Option("ssh.return", func() { f.exit = true })
|
|
||||||
f.ps1 = kit.Simple(m.Confv("prompt", "meta.PS1"))
|
|
||||||
f.ps2 = kit.Simple(m.Confv("prompt", "meta.PS2"))
|
|
||||||
ps := f.ps1
|
|
||||||
|
|
||||||
m.I, m.O = r, f.stdout
|
|
||||||
bio := bufio.NewScanner(r)
|
|
||||||
for f.prompt(m, ps...); bio.Scan() && !f.exit; f.prompt(m, ps...) {
|
|
||||||
if len(bio.Text()) == 0 {
|
|
||||||
continue // 空行
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(bio.Text(), "\\") {
|
|
||||||
line += bio.Text()[:len(bio.Text())-1]
|
|
||||||
ps = f.ps2
|
|
||||||
continue // 续行
|
|
||||||
}
|
|
||||||
if line += bio.Text(); strings.Count(line, "`")%2 == 1 {
|
|
||||||
line += "\n"
|
|
||||||
ps = f.ps2
|
|
||||||
continue // 多行
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(strings.TrimSpace(line), "#") {
|
|
||||||
line = ""
|
|
||||||
continue // 注释
|
|
||||||
}
|
|
||||||
// if line = f.history(m, line); line == "" {
|
|
||||||
// // 历史命令
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
if ps = f.ps1; f.stdout == os.Stdout {
|
|
||||||
// 清空格式
|
|
||||||
f.printf(m, "\033[0m")
|
|
||||||
}
|
|
||||||
line = f.parse(m, line)
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Frame) Begin(m *ice.Message, arg ...string) ice.Server {
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
func (f *Frame) Spawn(m *ice.Message, c *ice.Context, arg ...string) ice.Server {
|
|
||||||
return &Frame{}
|
|
||||||
}
|
|
||||||
func (f *Frame) Start(m *ice.Message, arg ...string) bool {
|
|
||||||
f.source, f.target = kit.Select(STDIO, arg, 0), m.Target()
|
|
||||||
|
|
||||||
var r io.Reader
|
|
||||||
switch m.Cap(ice.CTX_STREAM, f.source) {
|
|
||||||
case STDIO: // 终端交互
|
|
||||||
r, f.stdout = os.Stdin, os.Stdout
|
|
||||||
|
|
||||||
m.Option("_option", ice.MSG_USERNAME)
|
|
||||||
m.Option(ice.MSG_USERNAME, cli.UserName)
|
|
||||||
m.Option(ice.MSG_USERROLE, aaa.ROOT)
|
|
||||||
m.Option(ice.MSG_USERZONE, "boot")
|
|
||||||
aaa.UserRoot(m)
|
|
||||||
default:
|
|
||||||
f.target = m.Source()
|
|
||||||
|
|
||||||
if strings.HasPrefix(f.source, "/dev") {
|
|
||||||
r, f.stdout = m.I, m.O
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := bytes.NewBuffer(make([]byte, 0, 4096))
|
|
||||||
defer func() { m.Echo(buf.String()) }()
|
|
||||||
|
|
||||||
if s := Script(m, f.source); s != nil {
|
|
||||||
r, f.stdout = s, buf
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
f.scan(m, "", r)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
func (f *Frame) Close(m *ice.Message, arg ...string) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
STDIO = "stdio"
|
|
||||||
)
|
|
||||||
const (
|
|
||||||
SOURCE = "source"
|
|
||||||
TARGET = "target"
|
|
||||||
PROMPT = "prompt"
|
|
||||||
RETURN = "return"
|
|
||||||
)
|
|
||||||
const SSH = "ssh"
|
const SSH = "ssh"
|
||||||
|
|
||||||
var Index = &ice.Context{Name: SSH, Help: "终端模块",
|
var Index = &ice.Context{Name: SSH, Help: "终端模块", Commands: map[string]*ice.Command{
|
||||||
Configs: map[string]*ice.Config{
|
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||||
SOURCE: {Name: SOURCE, Help: "加载脚本", Value: kit.Data()},
|
m.Load()
|
||||||
PROMPT: {Name: PROMPT, Help: "命令提示", Value: kit.Data(
|
}},
|
||||||
"PS1", []interface{}{"\033[33;44m", "count", "[", "time", "]", "\033[5m", "target", "\033[0m", "\033[44m", ">", "\033[0m ", "\033[?25h", "\033[32m"},
|
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||||
"PS2", []interface{}{"count", " ", "target", "> "},
|
if _, ok := m.Target().Server().(*Frame); ok {
|
||||||
)},
|
m.Done()
|
||||||
},
|
}
|
||||||
Commands: map[string]*ice.Command{
|
m.Richs(CONNECT, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
|
||||||
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { m.Load() }},
|
if value[kit.MDB_META] != nil {
|
||||||
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
value = value[kit.MDB_META].(map[string]interface{})
|
||||||
if _, ok := m.Target().Server().(*Frame); ok {
|
|
||||||
m.Done()
|
|
||||||
}
|
}
|
||||||
m.Conf(SESSION, kit.MDB_HASH, "")
|
kit.Value(value, "status", tcp.CLOSE)
|
||||||
m.Conf(CONNECT, kit.MDB_HASH, "")
|
})
|
||||||
m.Save()
|
m.Richs(SESSION, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
|
||||||
}},
|
if value[kit.MDB_META] != nil {
|
||||||
|
value = value[kit.MDB_META].(map[string]interface{})
|
||||||
|
}
|
||||||
|
kit.Value(value, "status", tcp.CLOSE)
|
||||||
|
})
|
||||||
|
m.Save()
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
|
||||||
SOURCE: {Name: "source file", Help: "脚本解析", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
func init() {
|
||||||
m.Starts(strings.Replace(arg[0], ".", "_", -1), arg[0], arg[0:]...)
|
ice.Index.Register(Index, &Frame{},
|
||||||
}},
|
SOURCE, TARGET, PROMPT, RETURN,
|
||||||
TARGET: {Name: "target name", Help: "当前模块", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
CONNECT, SESSION, SERVICE,
|
||||||
m.Search(arg[0], func(p *ice.Context, s *ice.Context, key string) {
|
)
|
||||||
f := m.Target().Server().(*Frame)
|
|
||||||
f.target = s
|
|
||||||
f.prompt(m)
|
|
||||||
})
|
|
||||||
}},
|
|
||||||
PROMPT: {Name: "prompt arg...", Help: "命令提示", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
|
||||||
f := m.Target().Server().(*Frame)
|
|
||||||
f.ps1 = arg
|
|
||||||
f.prompt(m)
|
|
||||||
}},
|
|
||||||
RETURN: {Name: "return", Help: "结束脚本", Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
|
||||||
switch cb := m.Optionv("ssh.return").(type) {
|
|
||||||
case func():
|
|
||||||
cb()
|
|
||||||
}
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { ice.Index.Register(Index, &Frame{}, SOURCE, TARGET, RETURN) }
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
title "ssh"
|
chapter "ssh"
|
||||||
refer `
|
refer `
|
||||||
官网 http://www.openssh.com/
|
官网 http://www.openssh.com/
|
||||||
源码 https://github.com/openssh/openssh-portable
|
|
||||||
文档 https://man.openbsd.org/ssh
|
文档 https://man.openbsd.org/ssh
|
||||||
|
源码 https://github.com/openssh/openssh-portable
|
||||||
`
|
`
|
||||||
|
|
||||||
chapter "应用"
|
|
||||||
field "登录" ssh.dial
|
|
||||||
|
|
||||||
field "公钥" ssh.public
|
|
||||||
field "服务" ssh.listen
|
|
||||||
field "连接" ssh.connect
|
field "连接" ssh.connect
|
||||||
field "会话" ssh.session
|
field "会话" ssh.session
|
||||||
|
return
|
||||||
|
|
||||||
|
chapter "应用"
|
||||||
|
field "公钥" ssh.public
|
||||||
|
field "服务" ssh.listen
|
||||||
field "命令" ssh.command
|
field "命令" ssh.command
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,10 +18,16 @@ var Index = &ice.Context{Name: TCP, Help: "通信模块",
|
|||||||
}},
|
}},
|
||||||
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||||
m.Richs(CLIENT, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
|
m.Richs(CLIENT, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
|
||||||
kit.Value(value, "meta.status", CLOSE)
|
if value[kit.MDB_META] != nil {
|
||||||
|
value = value[kit.MDB_META].(map[string]interface{})
|
||||||
|
}
|
||||||
|
kit.Value(value, "status", CLOSE)
|
||||||
})
|
})
|
||||||
m.Richs(SERVER, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
|
m.Richs(SERVER, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
|
||||||
kit.Value(value, "meta.status", CLOSE)
|
if value[kit.MDB_META] != nil {
|
||||||
|
value = value[kit.MDB_META].(map[string]interface{})
|
||||||
|
}
|
||||||
|
kit.Value(value, "status", CLOSE)
|
||||||
})
|
})
|
||||||
m.Save()
|
m.Save()
|
||||||
}},
|
}},
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
chapter "tcp"
|
chapter "tcp"
|
||||||
|
|
||||||
field host tcp.host
|
field "主机" tcp.host
|
||||||
# field port tcp.port
|
field "端口" tcp.port
|
||||||
field server tcp.server
|
|
||||||
field client tcp.client
|
field "服务器" tcp.server
|
||||||
|
field "客户端" tcp.client
|
||||||
|
20
exec.go
20
exec.go
@ -109,13 +109,29 @@ func (m *Message) Back(res *Message) *Message {
|
|||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
func (m *Message) Gos(msg *Message, cb func(*Message)) *Message {
|
func (m *Message) Gos(msg *Message, cb interface{}) *Message {
|
||||||
m.Cmd("gdb.routine", "create", "fileline", kit.FileLine(cb, 3))
|
m.Cmd("gdb.routine", "create", "fileline", kit.FileLine(cb, 3))
|
||||||
|
|
||||||
task.Put(nil, func(task *task.Task) error {
|
task.Put(nil, func(task *task.Task) error {
|
||||||
msg.Optionv("_task", task)
|
msg.Optionv("_task", task)
|
||||||
msg.TryCatch(msg, true, func(msg *Message) { cb(msg) })
|
msg.TryCatch(msg, true, func(msg *Message) {
|
||||||
|
switch cb := cb.(type) {
|
||||||
|
case func(*Message):
|
||||||
|
cb(msg)
|
||||||
|
case func():
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
})
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
func (m *Message) Go(cb interface{}) *Message {
|
||||||
|
switch cb := cb.(type) {
|
||||||
|
case func(*Message):
|
||||||
|
return m.Gos(m.Spawn(), cb)
|
||||||
|
case func():
|
||||||
|
return m.Gos(m, cb)
|
||||||
|
}
|
||||||
|
return m.Gos(m, cb)
|
||||||
|
}
|
||||||
|
18
type.go
18
type.go
@ -114,7 +114,11 @@ func (c *Context) cmd(m *Message, cmd *Command, key string, arg ...string) *Mess
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Log(LOG_CMDS, "%s.%s %d %v %s", c.Name, key, len(arg), arg, kit.FileLine(cmd.Hand, 3))
|
if m.target.Name == "mdb" {
|
||||||
|
m.Log(LOG_CMDS, "%s.%s %d %v %s", c.Name, key, len(arg), arg, kit.FileLine(8, 3))
|
||||||
|
} else {
|
||||||
|
m.Log(LOG_CMDS, "%s.%s %d %v %s", c.Name, key, len(arg), arg, kit.FileLine(cmd.Hand, 3))
|
||||||
|
}
|
||||||
cmd.Hand(m, c, key, arg...)
|
cmd.Hand(m, c, key, arg...)
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
@ -620,12 +624,12 @@ func (m *Message) Search(key interface{}, cb interface{}) *Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Message) Cmdy(arg ...interface{}) *Message {
|
func (m *Message) Cmdy(arg ...interface{}) *Message {
|
||||||
return m.Copy(m.Cmd(arg...))
|
return m.Copy(m.__cmd(arg...))
|
||||||
}
|
}
|
||||||
func (m *Message) Cmdx(arg ...interface{}) string {
|
func (m *Message) Cmdx(arg ...interface{}) string {
|
||||||
return kit.Select("", m.Cmd(arg...).meta[MSG_RESULT], 0)
|
return kit.Select("", m.__cmd(arg...).meta[MSG_RESULT], 0)
|
||||||
}
|
}
|
||||||
func (m *Message) Cmd(arg ...interface{}) *Message {
|
func (m *Message) __cmd(arg ...interface{}) *Message {
|
||||||
list := kit.Simple(arg...)
|
list := kit.Simple(arg...)
|
||||||
if len(list) == 0 && m.Hand == false {
|
if len(list) == 0 && m.Hand == false {
|
||||||
list = m.meta[MSG_DETAIL]
|
list = m.meta[MSG_DETAIL]
|
||||||
@ -645,6 +649,12 @@ func (m *Message) Cmd(arg ...interface{}) *Message {
|
|||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
func (m *Message) Cmds(arg ...interface{}) *Message {
|
||||||
|
return m.Go(func() { m.__cmd(arg...) })
|
||||||
|
}
|
||||||
|
func (m *Message) Cmd(arg ...interface{}) *Message {
|
||||||
|
return m.__cmd(arg...)
|
||||||
|
}
|
||||||
func (m *Message) Confm(key string, chain interface{}, cbs ...interface{}) map[string]interface{} {
|
func (m *Message) Confm(key string, chain interface{}, cbs ...interface{}) map[string]interface{} {
|
||||||
val := m.Confv(key, chain)
|
val := m.Confv(key, chain)
|
||||||
if len(cbs) > 0 {
|
if len(cbs) > 0 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user