diff --git a/README.md b/README.md index 417ced47..9aba2412 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,7 @@ context: 通过提供自由的模块,简洁的接口,动态的结构,让 * detail[] option[] result[] append[] * Context Master Owner -* Search() Action() Assert() Figure() - - +* Search() Choice() Assert() Figure() ## 结构设计 * 功能树 @@ -36,11 +34,9 @@ context: 通过提供自由的模块,简洁的接口,动态的结构,让 * 消息树 ## 分支管理 -* 0.1 ctx cli -* 0.2 mdb tcp -* 0.3 aaa ssh -* 0.4 web nfs -* 0.5 lex yac +* 0.1 ctx cli aaa ssh +* 0.2 nfs tcp mdb web +* 0.3 lex yac log gdb ## 终端管理 * 寻址 指令 事件 函数 资源 diff --git a/etc/go.snippets b/etc/go.snippets index c8452b87..9cdde01e 100644 --- a/etc/go.snippets +++ b/etc/go.snippets @@ -39,6 +39,9 @@ snippet c } func (`Filename()` *`toupper(substitute(expand("%:t"), ".go", "", ""))`) Begin(m *ctx.Message, arg ...string) ctx.Server { + if `Filename()`.Context == Index { + Pulse = m + } return `Filename()` } @@ -46,11 +49,20 @@ snippet c return false } - func (`Filename()` *`toupper(substitute(expand("%:t"), ".go", "", ""))`) Exit(m *ctx.Message, arg ...string) bool { + func (`Filename()` *`toupper(substitute(expand("%:t"), ".go", "", ""))`) Close(m *ctx.Message, arg ...string) bool { + switch `Filename()`.Context { + case m.Target: + if `Filename()`.Context == Index { + return false + } + case m.Source: + return true + } return false } + var Pulse *ctx.Message var Index = &ctx.Context{Name: "`Filename()`", Help: "${1}", Caches: map[string]*ctx.Cache{}, Configs: map[string]*ctx.Config{}, @@ -67,6 +79,7 @@ snippet cmd ${4} return "" }}, + snippet conf "${1}": &ctx.Config{Name: "${2}", Value: "${3}", Help: "${4}", Hand: func(m *ctx.Message, x *ctx.Config, arg ...string) string { if len(arg) > 0 { diff --git a/etc/init.sh b/etc/init.sh index 9f7bdbc2..e2ca999f 100644 --- a/etc/init.sh +++ b/etc/init.sh @@ -1,13 +1,19 @@ -# ~lex source etc/lex.sh -~cli @lex lex -~root aaa login root root -# ~tcp listen ":9393" +~cli + @lex lex + +~aaa + login root root + +~tcp + listen :9393 + # ~tcp dial ":9393" -~cli remote slaver listen ":9393" tcp +# ~cli remote slaver listen ":9393" tcp # @debug on -~mdb open chat chat "chat:chat@/chat" mysql -~web listen +# ~mdb open chat chat "chat:chat@/chat" mysql +# ~web listen +# ~ssh listen :9898 return diff --git a/src/context/aaa/aaa.go b/src/context/aaa/aaa.go index 98f33b97..ba9f5a7b 100644 --- a/src/context/aaa/aaa.go +++ b/src/context/aaa/aaa.go @@ -44,7 +44,7 @@ func (aaa *AAA) Begin(m *ctx.Message, arg ...string) ctx.Server { // {{{ if len(arg) > 0 { // {{{ bs := md5.Sum([]byte(fmt.Sprintln("用户密码:%s", arg[0]))) m.Assert(x.Value == "" || x.Value == hex.EncodeToString(bs[:]), "密码错误") - m.Cap("expire", fmt.Sprintf("%d", time.Now().Unix()+int64(m.Confi("expire")))) + m.Cap("expire", fmt.Sprintf("%d", time.Now().Unix()+int64(Pulse.Confi("expire")))) return hex.EncodeToString(bs[:]) } return x.Value @@ -77,32 +77,36 @@ func (aaa *AAA) Start(m *ctx.Message, arg ...string) bool { // {{{ if len(arg) > 1 { if m.Cap("sessid") == "" { m.Cap("sessid", aaa.session(arg[1])) - m.Capi("nuser", 1) + Pulse.Capi("nuser", 1) } - m.Log("info", m.Source, "login %s %s", m.Cap("group", arg[0]), m.Cap("username", arg[1])) + m.Log("info", m.Source, "create %s %s", m.Cap("group", arg[0]), m.Cap("username", arg[1])) m.Cap("stream", m.Cap("username")) } + m.Log("info", m.Source, "login %s %s", m.Cap("group"), m.Cap("username")) return false } // }}} func (aaa *AAA) Close(m *ctx.Message, arg ...string) bool { // {{{ - if aaa.Context == Index { - return false - } - switch aaa.Context { case m.Target: + if aaa.Context == Index { + return false + } + + if len(aaa.Context.Requests) == 0 { + m.Log("info", nil, "%d logout %s", Pulse.Capi("nuser", -1)+1, m.Cap("username")) + } case m.Source: } - m.Log("info", nil, "%d logout %s", Pulse.Capi("nuser", -1)+1, m.Cap("username")) return true } // }}} +var Pulse *ctx.Message var Index = &ctx.Context{Name: "aaa", Help: "认证中心", Caches: map[string]*ctx.Cache{ "nuser": &ctx.Cache{Name: "用户数量", Value: "0", Help: "用户数量"}, @@ -131,6 +135,10 @@ var Index = &ctx.Context{Name: "aaa", Help: "认证中心", m.Source.Group, m.Source.Owner = m.Cap("group"), m.Target m.Log("info", m.Source, "logon %s", m.Cap("group"), m.Cap("username")) + if m.Name != "" { + c.Requests = append(c.Requests, m) + m.Index = len(m.Target.Requests) + } return m.Cap("username") } case 2, 3: @@ -139,12 +147,17 @@ var Index = &ctx.Context{Name: "aaa", Help: "认证中心", username, password = arg[1], arg[2] } - if username == m.Conf("rootname") { + if username == Pulse.Conf("rootname") { m.Set("detail", group, username).Target.Start(m) } else if msg := m.Find(username); msg == nil { m.Start(username, "认证用户", group, username) } else { m.Target = msg.Target + msg.Target.Start(msg) + if m.Name != "" { + m.Target.Requests = append(m.Target.Requests, m) + m.Index = len(m.Target.Requests) + } } m.Cap("password", password) @@ -162,7 +175,6 @@ var Index = &ctx.Context{Name: "aaa", Help: "认证中心", }, }, } -var Pulse *ctx.Message func init() { aaa := &AAA{} diff --git a/src/context/cli/cli.go b/src/context/cli/cli.go index b9f8c467..b4695538 100644 --- a/src/context/cli/cli.go +++ b/src/context/cli/cli.go @@ -239,6 +239,9 @@ func (cli *CLI) Begin(m *ctx.Message, arg ...string) ctx.Server { // {{{ if cli.Context != Index { cli.Owner = nil } + if cli.Context == Index { + Pulse = m + } cli.target = cli.Context cli.alias = map[string]string{ @@ -286,6 +289,7 @@ func (cli *CLI) Start(m *ctx.Message, arg ...string) bool { // {{{ cli.echo("password>") fmt.Fscanln(cli.in, &password) + msg.Name = "aaa" msg.Wait = make(chan bool) if msg.Cmd("login", username, password) == "" { cli.echo("登录失败") @@ -299,7 +303,6 @@ func (cli *CLI) Start(m *ctx.Message, arg ...string) bool { // {{{ cli.Sessions = make(map[string]*ctx.Message) } cli.Sessions["aaa"] = msg - msg.Name = "aaa" } } else { m.Cap("stream", "stdout") @@ -352,13 +355,23 @@ func (cli *CLI) Start(m *ctx.Message, arg ...string) bool { // {{{ // }}} func (cli *CLI) Close(m *ctx.Message, arg ...string) bool { // {{{ - if cli.Context == Index { - return false - } - switch cli.Context { case m.Target: + if cli.Context == Index { + return false + } + + if len(cli.Context.Requests) == 0 { + m.Log("info", nil, "%s close %v", Pulse.Cap("nterm"), arg) + } case m.Source: + if m.Name == "aaa" { + msg := m.Spawn(cli.Context) + msg.Master = cli.Context + if !cli.Context.Close(msg, arg...) { + return false + } + } } return true @@ -366,6 +379,7 @@ func (cli *CLI) Close(m *ctx.Message, arg ...string) bool { // {{{ // }}} +var Pulse *ctx.Message var Index = &ctx.Context{Name: "cli", Help: "管理中心", Caches: map[string]*ctx.Cache{ "nterm": &ctx.Cache{Name: "终端数量", Value: "0", Help: "已经运行的终端数量"}, diff --git a/src/context/ctx.go b/src/context/ctx.go index 4c1e871f..0b14dca8 100644 --- a/src/context/ctx.go +++ b/src/context/ctx.go @@ -55,24 +55,23 @@ type Context struct { Configs map[string]*Config Commands map[string]*Command - contexts map[string]*Context - context *Context root *Context + context *Context + contexts map[string]*Context - Sessions map[string]*Message - Historys []*Message - Requests []*Message - - messages chan *Message - message *Message Master *Context + messages chan *Message + + Pulse *Message + Requests []*Message + Historys []*Message + Sessions map[string]*Message Index map[string]*Context Groups map[string]*Context Owner *Context Group string - Pulse *Message Server } @@ -87,24 +86,20 @@ func (c *Context) Register(s *Context, x Server) *Context { // {{{ c.contexts[s.Name] = s s.context = c s.Server = x - - if c.root != nil { - s.root = c.root - } return s } // }}} func (c *Context) Spawn(m *Message, name string, help string) *Context { // {{{ - s := &Context{Name: name, Help: help, context: c} + s := &Context{Name: name, Help: help, root: c.root, context: c} - if c.Server != nil { + if m.Target = s; c.Server != nil { c.Register(s, c.Server.Spawn(m, s, m.Meta["detail"]...)) } else { c.Register(s, nil) } - if m.Target = s; m.Template != nil { + if m.Template != nil { m.Template.Source = s } @@ -116,20 +111,22 @@ func (c *Context) Begin(m *Message) *Context { // {{{ c.Caches["status"] = &Cache{Name: "服务状态(begin/start/close)", Value: "begin", Help: "服务状态,begin:初始完成,start:正在运行,close:未在运行"} c.Caches["stream"] = &Cache{Name: "服务数据", Value: "", Help: "服务数据"} + m.Index = 1 + c.Pulse = m + c.Requests = []*Message{m} + c.Historys = []*Message{m} + c.Master = m.Master.Master c.Owner = m.Master.Owner c.Group = m.Master.Group - m.Log("begin", nil, "%d %v", m.Capi("ncontext", 1), m.Meta["detail"]) + m.Log("begin", nil, "%d context %v", m.Capi("ncontext", 1), m.Meta["detail"]) for k, x := range c.Configs { if x.Hand != nil { m.Conf(k, x.Value) } } - c.Requests = []*Message{m} - c.Historys = []*Message{m} - if c.Server != nil { c.Server.Begin(m, m.Meta["detail"]...) } @@ -139,34 +136,39 @@ func (c *Context) Begin(m *Message) *Context { // {{{ // }}} func (c *Context) Start(m *Message) bool { // {{{ - if m.Cap("status") != "start" && c.Server != nil { + if c.Requests = append(c.Requests, m); m.Cap("status") != "start" { running := make(chan bool) go m.AssertOne(m, true, func(m *Message) { - m.Log(m.Cap("status", "start"), c, "%d %v", m.Capi("nserver", 1), m.Meta["detail"]) + m.Log(m.Cap("status", "start"), nil, "%d server %v", m.Capi("nserver", 1), m.Meta["detail"]) - if m != c.Requests[0] { - c.Requests = append(c.Requests, m) - } - - if running <- true; c.Server.Start(m, m.Meta["detail"]...) { + if running <- true; c.Server != nil && c.Server.Start(m, m.Meta["detail"]...) { c.Close(m, m.Meta["detail"]...) } }) <-running } - return true } // }}} func (c *Context) Close(m *Message, arg ...string) bool { // {{{ - m.Log("close", c, "close %v", arg) + m.Log("close", c, "%v", arg) + if m.Target == c { - for k, v := range c.Sessions { - delete(c.Sessions, k) - if v.Target != c && !v.Target.Close(v, arg...) { - return false + if m.Index == 0 { + for i := len(c.Requests) - 1; i >= 0; i-- { + v := c.Requests[i] + if v.Index = -1; v.Source != c && !v.Source.Close(v, arg...) { + v.Index = i + return false + } + c.Requests = c.Requests[:i] } + } else if m.Index > 0 { + for i := m.Index - 1; i < len(c.Requests)-1; i++ { + c.Requests[i] = c.Requests[i+1] + } + c.Requests = c.Requests[:len(c.Requests)-1] } } @@ -174,37 +176,30 @@ func (c *Context) Close(m *Message, arg ...string) bool { // {{{ return false } - if m.Source == c { + if m.Source == c && m.Target != c { if _, ok := c.Sessions[m.Name]; ok { delete(c.Sessions, m.Name) - m.Target.Server.Close(m, arg...) } - } else { - if m.Index == -1 { - return true - } - } - - if len(c.Sessions) > 0 { - return false - } - - if m.Cap("status") == "close" { return true } - if c.context != nil && len(c.contexts) == 0 { - m.Log("close", c, "%d context %v", m.root.Capi("ncontext", -1)+1, arg) - delete(c.context.contexts, c.Name) + if len(c.Requests) > 1 { + return false } - for _, v := range c.Requests { - if v.Source != c && v.Index != -1 { - v.Index = -1 - v.Source.Close(v, arg...) + if m.Cap("status") == "start" { + m.Log(m.Cap("status", "close"), nil, "%d server %v", m.root.Capi("nserver", -1)+1, arg) + for _, v := range c.Sessions { + if v.Target != c { + v.Target.Close(v, arg...) + } } } + if m.Index == 0 && c.context != nil && len(c.contexts) == 0 { + m.Log("close", nil, "%d context %v", m.root.Capi("ncontext", -1)+1, arg) + delete(c.context.contexts, c.Name) + } return true } @@ -388,6 +383,7 @@ type Message struct { code int time time.Time + Recv chan bool Wait chan bool Meta map[string][]string Data map[string]interface{} @@ -534,7 +530,7 @@ func (m *Message) Assert(e interface{}, msg ...string) bool { // {{{ e = errors.New("error") } - m.Set("result", "error: ", fmt.Sprintln(e)) + m.Set("result", "error: ", fmt.Sprintln(e), "\n") panic(e) } @@ -828,7 +824,7 @@ func (m *Message) End(s bool) { // {{{ func (m *Message) Exec(key string, arg ...string) string { // {{{ - for _, c := range []*Context{m.Target, m.Target.Master, m.Source, m.Source.Master} { + for _, c := range []*Context{m.Target, m.Target.Master, m.Target.Owner, m.Source, m.Source.Master, m.Source.Owner} { for s := c; s != nil; s = s.context { m.Master = m.Source @@ -1077,7 +1073,8 @@ func (m *Message) Cap(key string, arg ...string) string { // {{{ // }}} -var Index = &Context{Name: "ctx", Help: "元始模块", +var Pulse = &Message{code: 0, time: time.Now(), Wait: make(chan bool), Source: Index, Master: Index, Target: Index} +var Index = &Context{Name: "ctx", Help: "模块中心", Caches: map[string]*Cache{ "nserver": &Cache{Name: "服务数量", Value: "0", Help: "显示已经启动运行模块的数量"}, "ncontext": &Cache{Name: "模块数量", Value: "0", Help: "显示功能树已经注册模块的数量"}, @@ -1562,8 +1559,6 @@ var Index = &Context{Name: "ctx", Help: "元始模块", }, } -var Pulse = &Message{code: 0, time: time.Now(), Wait: make(chan bool), Source: Index, Master: Index, Target: Index} - func init() { Pulse.root = Pulse Index.root = Index diff --git a/src/context/ssh/ssh.go b/src/context/ssh/ssh.go index 174ad3a6..8d1666a3 100644 --- a/src/context/ssh/ssh.go +++ b/src/context/ssh/ssh.go @@ -1,39 +1,153 @@ -package ssh - -import ( +package ssh // {{{ +// }}} +import ( // {{{ + "bufio" "context" + "fmt" + "net" + "net/url" + "strings" ) +// }}} + type SSH struct { + send map[string]*ctx.Message + *bufio.Reader + net.Conn *ctx.Context } -func (ssh *SSH) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server { - c.Caches = map[string]*ctx.Cache{} +func (ssh *SSH) Spawn(m *ctx.Message, c *ctx.Context, arg ...string) ctx.Server { // {{{ + c.Caches = map[string]*ctx.Cache{ + "nsend": &ctx.Cache{Name: "nsend", Value: "0", Help: "消息发送数量"}, + } c.Configs = map[string]*ctx.Config{} c.Commands = map[string]*ctx.Command{} s := new(SSH) s.Context = c + + s.send = make(map[string]*ctx.Message) return s } -func (ssh *SSH) Begin(m *ctx.Message, arg ...string) ctx.Server { +// }}} +func (ssh *SSH) Begin(m *ctx.Message, arg ...string) ctx.Server { // {{{ + if ssh.Context == Index { + Pulse = m + } return ssh } -func (ssh *SSH) Start(m *ctx.Message, arg ...string) bool { +// }}} +func (ssh *SSH) Start(m *ctx.Message, arg ...string) bool { // {{{ + ssh.Owner = nil + ssh.Conn = m.Data["io"].(net.Conn) + ssh.Reader = bufio.NewReader(ssh.Conn) + m.Log("info", nil, "%d remote %v", 0, ssh.Conn.RemoteAddr()) + + target := m.Target + msg := m.Spawn(target) + + for { + line, e := ssh.Reader.ReadString('\n') + m.Assert(e) + + if line = strings.TrimSpace(line); len(line) == 0 { + if msg.Has("detail") { + msg.Log("info", nil, "remote: %v", msg.Meta["detail"]) + msg.Log("info", nil, "remote: %v", msg.Meta["option"]) + msg.Cmd(msg.Meta["detail"]...) + target = msg.Target + + fmt.Fprintf(ssh.Conn, "result: ") + for _, v := range msg.Meta["result"] { + fmt.Fprintf(ssh.Conn, "%s", url.QueryEscape(v)) + } + fmt.Fprintf(ssh.Conn, "\n") + + msg.Meta["append"] = append(msg.Meta["append"], "nsend") + msg.Add("append", "nsend", msg.Get("nsend")) + for _, k := range msg.Meta["append"] { + for _, v := range msg.Meta[k] { + fmt.Fprintf(ssh.Conn, "%s: %s\n", k, v) + } + } + fmt.Fprintf(ssh.Conn, "\n") + } else if msg.Has("result") { + msg.Log("info", nil, "remote: %v", msg.Meta["result"]) + msg.Log("info", nil, "remote: %v", msg.Meta["append"]) + send := ssh.send[msg.Get("nsend")] + send.Meta = msg.Meta + send.Recv <- true + } + msg = m.Spawn(target) + continue + } + + ls := strings.SplitN(line, ":", 2) + ls[0] = strings.TrimSpace(ls[0]) + ls[1], e = url.QueryUnescape(strings.TrimSpace(ls[1])) + m.Assert(e) + msg.Add("option", ls[0], ls[1]) + } return false } -func (ssh *SSH) Close(m *ctx.Message, arg ...string) bool { +// }}} +func (ssh *SSH) Close(m *ctx.Message, arg ...string) bool { // {{{ return false } -var Index = &ctx.Context{Name: "ssh", Help: "加密终端", - Caches: map[string]*ctx.Cache{}, - Configs: map[string]*ctx.Config{}, - Commands: map[string]*ctx.Command{}, +// }}} + +var Pulse *ctx.Message +var Index = &ctx.Context{Name: "ssh", Help: "集群中心", + Caches: map[string]*ctx.Cache{ + "nhost": &ctx.Cache{Name: "nhost", Value: "0", Help: "主机数量"}, + }, + Configs: map[string]*ctx.Config{}, + Commands: map[string]*ctx.Command{ + "listen": &ctx.Command{Name: "listen address", Help: "监听连接", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) string { + tcp := m.Find("tcp", true) // {{{ + tcp.Cmd(m.Meta["detail"]...) + return "" + // }}} + }}, + "dial": &ctx.Command{Name: "dial address", Help: "建立连接", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) string { + tcp := m.Find("tcp", true) // {{{ + tcp.Cmd(m.Meta["detail"]...) + return "" + // }}} + }}, + "open": &ctx.Command{Name: "open", Help: "打开连接", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) string { + m.Start("host"+Pulse.Cap("nhost"), "主机连接") // {{{ + return "" + // }}} + }}, + "remote": &ctx.Command{Name: "remote detail...", Help: "远程执行", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) string { + ssh, ok := m.Target.Server.(*SSH) // {{{ + m.Assert(ok) + m.Capi("nsend", 1) + m.Recv = make(chan bool) + m.Add("option", "nsend", m.Cap("nsend")) + ssh.send[m.Cap("nsend")] = m + + for _, v := range arg { + fmt.Fprintf(ssh.Conn, "detail: %v\n", v) + } + for _, k := range m.Meta["option"] { + for _, v := range m.Meta[k] { + fmt.Fprintf(ssh.Conn, "%s: %s\n", k, v) + } + } + fmt.Fprintf(ssh.Conn, "\n") + <-m.Recv + return "" + // }}} + }}, + }, } func init() { diff --git a/src/example/bench.go b/src/example/bench.go index e8f8ee84..38e9ed1d 100644 --- a/src/example/bench.go +++ b/src/example/bench.go @@ -4,13 +4,13 @@ import ( "context" _ "context/aaa" _ "context/cli" + _ "context/ssh" _ "context/mdb" + _ "context/nfs" _ "context/tcp" _ "context/web" - _ "context/ssh" - _ "context/lex" "os"