mirror of
https://shylinux.com/x/icebergs
synced 2025-05-02 19:47:02 +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()
|
||||
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) {
|
||||
m.Gos(m, func(m *ice.Message) {
|
||||
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"))
|
||||
list := []string{"PATH=" + os.Getenv("PATH")}
|
||||
|
||||
tty, f, err := pty.Open()
|
||||
pty, tty, err := pty.Open()
|
||||
if m.Warn(err != nil, err) {
|
||||
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 })
|
||||
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":
|
||||
termLen := request.Payload[3]
|
||||
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)
|
||||
|
||||
case "window-change":
|
||||
_ssh_size(tty.Fd(), request.Payload)
|
||||
_ssh_size(pty.Fd(), request.Payload)
|
||||
|
||||
case "env":
|
||||
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() })
|
||||
case "shell":
|
||||
if meta["username"] == "ssh" {
|
||||
m.I, m.O = f, f
|
||||
m.I, m.O = tty, tty
|
||||
m.Render(ice.RENDER_VOID)
|
||||
m.Gos(m, func(m *ice.Message) {
|
||||
m.Cmdy(SOURCE, tty.Name())
|
||||
m.Cmdy(SOURCE, pty.Name())
|
||||
_ssh_close(m, c, channel)
|
||||
})
|
||||
} 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")
|
||||
_ssh_close(m, c, channel)
|
||||
})
|
||||
}
|
||||
|
||||
m.Gos(m, func(m *ice.Message) { io.Copy(channel, tty) })
|
||||
_ssh_watch(m, meta, channel, tty, channel)
|
||||
// _ssh_trace(m, meta, channel, tty, channel)
|
||||
m.Gos(m, func(m *ice.Message) { io.Copy(channel, pty) })
|
||||
_ssh_watch(m, meta, channel, pty, channel)
|
||||
}
|
||||
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 {
|
||||
config := &ssh.ServerConfig{
|
||||
BannerCallback: func(conn ssh.ConnMetadata) string {
|
||||
@ -250,28 +191,10 @@ func _ssh_config(m *ice.Message) *ssh.ServerConfig {
|
||||
}
|
||||
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 (
|
||||
ADDRESS = "address"
|
||||
CONNECT = "connect"
|
||||
CHANNEL = "channel"
|
||||
SESSION = "session"
|
||||
REQUEST = "request"
|
||||
COMMAND = "command"
|
||||
)
|
||||
@ -295,19 +218,9 @@ func init() {
|
||||
LISTEN: {Name: LISTEN, Help: "服务", Value: kit.Data(kit.MDB_SHORT, aaa.HOSTPORT,
|
||||
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(
|
||||
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{
|
||||
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)
|
||||
}},
|
||||
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{
|
||||
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")
|
||||
@ -390,61 +283,6 @@ func init() {
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
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 (
|
||||
ice "github.com/shylinux/icebergs"
|
||||
"github.com/shylinux/icebergs/base/aaa"
|
||||
"github.com/shylinux/icebergs/base/cli"
|
||||
"github.com/shylinux/icebergs/base/mdb"
|
||||
"github.com/shylinux/icebergs/base/tcp"
|
||||
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"
|
||||
|
||||
var Index = &ice.Context{Name: SSH, Help: "终端模块",
|
||||
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{
|
||||
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) { m.Load() }},
|
||||
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||
if _, ok := m.Target().Server().(*Frame); ok {
|
||||
m.Done()
|
||||
var Index = &ice.Context{Name: SSH, Help: "终端模块", Commands: map[string]*ice.Command{
|
||||
ice.CTX_INIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||
m.Load()
|
||||
}},
|
||||
ice.CTX_EXIT: {Hand: func(m *ice.Message, c *ice.Context, cmd string, arg ...string) {
|
||||
if _, ok := m.Target().Server().(*Frame); ok {
|
||||
m.Done()
|
||||
}
|
||||
m.Richs(CONNECT, "", kit.MDB_FOREACH, func(key string, value map[string]interface{}) {
|
||||
if value[kit.MDB_META] != nil {
|
||||
value = value[kit.MDB_META].(map[string]interface{})
|
||||
}
|
||||
m.Conf(SESSION, kit.MDB_HASH, "")
|
||||
m.Conf(CONNECT, kit.MDB_HASH, "")
|
||||
m.Save()
|
||||
}},
|
||||
kit.Value(value, "status", tcp.CLOSE)
|
||||
})
|
||||
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) {
|
||||
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()
|
||||
}
|
||||
}},
|
||||
},
|
||||
func init() {
|
||||
ice.Index.Register(Index, &Frame{},
|
||||
SOURCE, TARGET, PROMPT, RETURN,
|
||||
CONNECT, SESSION, SERVICE,
|
||||
)
|
||||
}
|
||||
|
||||
func init() { ice.Index.Register(Index, &Frame{}, SOURCE, TARGET, RETURN) }
|
||||
|
@ -1,17 +1,17 @@
|
||||
title "ssh"
|
||||
chapter "ssh"
|
||||
refer `
|
||||
官网 http://www.openssh.com/
|
||||
源码 https://github.com/openssh/openssh-portable
|
||||
文档 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.session
|
||||
return
|
||||
|
||||
chapter "应用"
|
||||
field "公钥" ssh.public
|
||||
field "服务" ssh.listen
|
||||
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) {
|
||||
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{}) {
|
||||
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()
|
||||
}},
|
||||
|
@ -1,6 +1,7 @@
|
||||
chapter "tcp"
|
||||
|
||||
field host tcp.host
|
||||
# field port tcp.port
|
||||
field server tcp.server
|
||||
field client tcp.client
|
||||
field "主机" tcp.host
|
||||
field "端口" tcp.port
|
||||
|
||||
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
|
||||
}
|
||||
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))
|
||||
|
||||
task.Put(nil, func(task *task.Task) error {
|
||||
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 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...)
|
||||
return m
|
||||
}
|
||||
@ -620,12 +624,12 @@ func (m *Message) Search(key interface{}, cb 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 {
|
||||
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...)
|
||||
if len(list) == 0 && m.Hand == false {
|
||||
list = m.meta[MSG_DETAIL]
|
||||
@ -645,6 +649,12 @@ func (m *Message) Cmd(arg ...interface{}) *Message {
|
||||
}
|
||||
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{} {
|
||||
val := m.Confv(key, chain)
|
||||
if len(cbs) > 0 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user