diff --git a/base/mdb/mdb.shy b/base/mdb/mdb.shy new file mode 100644 index 00000000..23c5127a --- /dev/null +++ b/base/mdb/mdb.shy @@ -0,0 +1,8 @@ +chapter "mdb" + +field "搜索" mdb.search +field "引擎" mdb.engine + +field "插件" mdb.plugin +field "渲染" mdb.render + diff --git a/base/nfs/nfs.shy b/base/nfs/nfs.shy new file mode 100644 index 00000000..f6835613 --- /dev/null +++ b/base/nfs/nfs.shy @@ -0,0 +1,5 @@ +chapter "nfs" + +field "目录" nfs.dir +field "文件" nfs.file + diff --git a/base/ssh/connect.go b/base/ssh/connect.go new file mode 100644 index 00000000..64129986 --- /dev/null +++ b/base/ssh/connect.go @@ -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) +} diff --git a/base/ssh/script.go b/base/ssh/script.go new file mode 100644 index 00000000..b72a9d70 --- /dev/null +++ b/base/ssh/script.go @@ -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) +} diff --git a/base/ssh/server.go b/base/ssh/server.go index 04c5fcd1..62330adf 100644 --- a/base/ssh/server.go +++ b/base/ssh/server.go @@ -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) } diff --git a/base/ssh/service.go b/base/ssh/service.go new file mode 100644 index 00000000..1fa138cc --- /dev/null +++ b/base/ssh/service.go @@ -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) +} diff --git a/base/ssh/session.go b/base/ssh/session.go new file mode 100644 index 00000000..d3087eaa --- /dev/null +++ b/base/ssh/session.go @@ -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) +} diff --git a/base/ssh/ssh.go b/base/ssh/ssh.go index 6e2aee00..b1e5cf1e 100644 --- a/base/ssh/ssh.go +++ b/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) } diff --git a/base/ssh/ssh.shy b/base/ssh/ssh.shy index f2178e2c..cc577438 100644 --- a/base/ssh/ssh.shy +++ b/base/ssh/ssh.shy @@ -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 diff --git a/base/tcp/tcp.go b/base/tcp/tcp.go index 92019641..bbd2e07c 100644 --- a/base/tcp/tcp.go +++ b/base/tcp/tcp.go @@ -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() }}, diff --git a/base/tcp/tcp.shy b/base/tcp/tcp.shy index 5e20b5c4..5a14476d 100644 --- a/base/tcp/tcp.shy +++ b/base/tcp/tcp.shy @@ -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 diff --git a/exec.go b/exec.go index e1067b2f..ef91ddbc 100644 --- a/exec.go +++ b/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) +} diff --git a/type.go b/type.go index ce395615..481ba897 100644 --- a/type.go +++ b/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 {