commit c4e8a4b718e135e3f8059c35cf53f18caed366e4 Author: shaoying Date: Wed Nov 1 00:08:08 2017 +0800 创建上下文,支持消息处理、服务启动、自动级联、路径搜索 diff --git a/bench.go b/bench.go new file mode 100644 index 00000000..b735a205 --- /dev/null +++ b/bench.go @@ -0,0 +1,20 @@ +package main + +import ( + _ "context" + "context/cli" + _ "context/ssh" + "os" +) + +func main() { + if len(os.Args) > 1 { + cli.Index.Conf("log", os.Args[1]) + } + + if len(os.Args) > 2 { + cli.Index.Conf("init.sh", os.Args[2]) + } + + cli.Index.Start() +} diff --git a/etc/cert.pem b/etc/cert.pem new file mode 100644 index 00000000..98139d4c --- /dev/null +++ b/etc/cert.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC9TCCAl4CCQCHSqshz+HyLTANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +R0IxHzAdBgNVBAgTFlRlc3QgU3RhdGUgb3IgUHJvdmluY2UxFjAUBgNVBAcTDVRl +c3QgTG9jYWxpdHkxGjAYBgNVBAoTEU9yZ2FuaXphdGlvbiBOYW1lMSEwHwYDVQQL +ExhPcmdhbml6YXRpb25hbCBVbml0IE5hbWUxFDASBgNVBAMTC0NvbW1vbiBOYW1l +MSEwHwYJKoZIhvcNAQkBFhJ0ZXN0QGVtYWlsLmFkZHJlc3MwHhcNMTcxMDMxMTYw +NDM5WhcNMTcxMTMwMTYwNDM5WjCBvjELMAkGA1UEBhMCR0IxHzAdBgNVBAgTFlRl +c3QgU3RhdGUgb3IgUHJvdmluY2UxFjAUBgNVBAcTDVRlc3QgTG9jYWxpdHkxGjAY +BgNVBAoTEU9yZ2FuaXphdGlvbiBOYW1lMSEwHwYDVQQLExhPcmdhbml6YXRpb25h +bCBVbml0IE5hbWUxFDASBgNVBAMTC0NvbW1vbiBOYW1lMSEwHwYJKoZIhvcNAQkB +FhJ0ZXN0QGVtYWlsLmFkZHJlc3MwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +AOw3gvdtfKWkSEl2l30V7irBhkrD6IVd6AzxaAYL97giDglPvu7ng2PXYlF5pjjf +mxDYtjAGuq1itnN0LKRe6CjUOuGtC2KMlZ8121fQCNw8M6TLPSpDjVuzysaUb2ds ++OClb0uC8SmSy3bOCGsicI77yXvEuKFkm43ikyVounmRAgMBAAEwDQYJKoZIhvcN +AQELBQADgYEAkOk7DVR/XgJdMSXXGd/OtWmTfVp2sIyyy37zSoc4uRFWwPqbzLPf +NgUKGNHEYvJY7/bWQ3p2D+u1U2PUfv/t6SQcAu3Nkw7sd7PoeeDZcRau84NevgoR +HfQKirJQgZd0hKFwiBnDspYbi8IL2mHEJOlzw1priY9v8MVIscyFVbE= +-----END CERTIFICATE----- diff --git a/etc/he.sh b/etc/he.sh new file mode 100644 index 00000000..31dd88e5 --- /dev/null +++ b/etc/he.sh @@ -0,0 +1,9 @@ +alias ~ context +alias ! history +alias @ config +alias $ cache +alias & server +alias * message + +~ssh +& diff --git a/etc/hi.sh b/etc/hi.sh new file mode 100644 index 00000000..c10ba31b --- /dev/null +++ b/etc/hi.sh @@ -0,0 +1,10 @@ +alias ~ context +alias ! history +alias @ config +alias $ cache +alias & server +alias * message + +~ssh +@client no +& diff --git a/etc/key.pem b/etc/key.pem new file mode 100644 index 00000000..ae38b53e --- /dev/null +++ b/etc/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXwIBAAKBgQDsN4L3bXylpEhJdpd9Fe4qwYZKw+iFXegM8WgGC/e4Ig4JT77u +54Nj12JReaY435sQ2LYwBrqtYrZzdCykXugo1DrhrQtijJWfNdtX0AjcPDOkyz0q +Q41bs8rGlG9nbPjgpW9LgvEpkst2zghrInCO+8l7xLihZJuN4pMlaLp5kQIDAQAB +AoGBAOasYwG68pFTN6A95jupsdYg/EKAw82RYa1aBUp6X2N6JhjjvkHQ5ZcXWxTT +ZgZ+HhC6gFewCpaNIjzmwz2UzMJJ4empijEbJFuwZCq4M/Adca2BhduV2YIwqvi8 +MHGmHMB81zYKA0E4j+vahJnn8aAqSoPnaM9MBw5vhggU5YodAkEA/IwRTVNOHmIm +1XCfvPoSpDBpSJh6V1mPyBuPs/2Fr52j+L+6qOhNML0O063az44/dR8RZytcaGYQ +7EByYbeCZwJBAO9ySW4TbDLRejSmFWHmflrnjV7s4DqE6OBbCRJF3aIleELYaTPC +Q0kOKRZTCr1PwopIdAOOOgaSWgsX75zU2UcCQQDMCwb3qLTXC4pArMwCzTE+gvat +drRx2qS2kr4aOF1ItF8E3TOcwIONO1K9aBv/0fgnUsCm0HvKxZwqpS9FEBVFAkEA +ntgeRlu0J3I3s72J6cxSflOlwRc7GRcatdsuhWS7xtk8knumLqPspwYx05F7SmMj +F0FBVSqA6+MiwME8P7oj+QJBAJFv2HKAaGElXkaJJzmQPHGJdGLUMb9oHXPtzcpz +HLtT2kHK1LlQqsOEacivPCKtnnLkX6Xsl8pMpe8EV43t718= +-----END RSA PRIVATE KEY----- diff --git a/src/context/cli/cli.go b/src/context/cli/cli.go new file mode 100644 index 00000000..7d23aa6f --- /dev/null +++ b/src/context/cli/cli.go @@ -0,0 +1,513 @@ +package cli // {{{ +// }}} +import ( // {{{ + "bufio" + "context" + "fmt" + "io" + "log" + "os" + "os/exec" + "strconv" + "strings" + "time" + "unicode" +) + +// }}} + +type CLI struct { + in *os.File + ins []*os.File + bio *bufio.Reader + bios []*bufio.Reader + out *os.File + + index int + history []map[string]string + alias map[string]string + next string + + loop int + + target *ctx.Context + exit chan bool + *ctx.Context +} + +func (cli *CLI) push(f *os.File) { // {{{ + if cli.ins == nil || cli.bios == nil { + cli.ins = make([]*os.File, 0, 3) + cli.bios = make([]*bufio.Reader, 0, 3) + } + + cli.in = f + cli.ins = append(cli.ins, cli.in) + cli.bio = bufio.NewReader(f) + cli.bios = append(cli.bios, cli.bio) +} + +// }}} +func (cli *CLI) parse() bool { // {{{ + if len(cli.ins) == 1 { + cli.echo(cli.Conf("PS1")) + } + + line := "" + if cli.next == "" { + l, e := cli.bio.ReadString('\n') + if e == io.EOF { + l := len(cli.ins) + if l == 1 { + if cli.Conf("slient") != "yes" { + cli.echo("\n") + cli.echo(cli.Conf("结束语")) + } + cli.echo("\n") + cli.exit <- true + return false + + } else { + cli.ins = cli.ins[:l-1] + cli.bios = cli.bios[:l-1] + cli.in = cli.ins[l-2] + cli.bio = cli.bios[l-2] + return true + } + } + cli.Check(e) + line = l + } else { + line = cli.next + if len(cli.ins) == 1 { + cli.echo(line) + cli.echo("\n") + } + } + + if len(cli.ins) > 1 { + cli.echo(cli.Conf("PS1")) + cli.echo(line) + } + cli.next = "" + + if len(line) == 1 { + return true + } + + line = strings.TrimSpace(line) + if line[0] == '#' { + return true + } + ls := strings.Split(line, " ") + + msg := &ctx.Message{Wait: make(chan bool)} + msg.Context = cli.Context + + r := rune(ls[0][0]) + if !unicode.IsNumber(r) || !unicode.IsLetter(r) || r == '$' || r == '_' { + if _, ok := cli.alias[string(r)]; ok { + msg.Add("detail", ls[0][:1]) + if len(ls[0]) > 1 { + ls[0] = ls[0][1:] + } else { + if len(ls) > 1 { + ls = ls[1:] + } else { + ls = nil + } + } + } + } + + for i := 0; i < len(ls); i++ { + ls[i] = strings.TrimSpace(ls[i]) + if ls[i][0] == '#' { + break + } + if ls[i] != "" { + msg.Add("detail", ls[i]) + } + } + + cli.Context.Messages <- msg + <-msg.Wait + + for _, v := range msg.Meta["result"] { + cli.echo(v) + } + + return true +} + +// }}} +func (cli *CLI) deal(msg *ctx.Message) bool { // {{{ + defer func() { + if e := recover(); e != nil { + msg.Echo("%s", e) + log.Println(e) + } + }() + + detail := msg.Meta["detail"] + switch cli.Conf("mode") { + case "local": + if a, ok := cli.alias[detail[0]]; ok { + detail[0] = a + } + + if _, ok := cli.Commands[detail[0]]; ok { + cli.loop = 0 + cli.next = cli.Cmd(msg, detail...) + } else if _, ok := cli.target.Commands[detail[0]]; ok { + cli.loop = 0 + cli.target.Message = msg + cli.next = cli.target.Cmd(msg, detail...) + } else { + cmd := exec.Command(detail[0], detail[1:]...) + v, e := cmd.CombinedOutput() + if e != nil && cli.loop < 1 { + cli.next = cli.Conf("default") + " " + strings.Join(detail, " ") + cli.loop++ + } + msg.Echo(string(v)) + log.Println(cli.Name, "command:", detail) + } + } + cli.history = append(cli.history, map[string]string{ + "time": time.Now().Format("15:04:05"), + "index": fmt.Sprintf("%d", cli.index), + "cli": strings.Join(detail, " "), + }) + cli.index++ + return true +} + +// }}} +func (cli *CLI) echo(str string, arg ...interface{}) { // {{{ + if len(cli.ins) == 1 || cli.Conf("slient") != "yes" { + fmt.Fprintf(cli.out, str, arg...) + } +} + +// }}} + +func (cli *CLI) Begin() bool { // {{{ + // cli.Conf("log", cli.Conf("log")) + for k, v := range cli.Configs { + cli.Conf(k, v.Value) + } + + if cli.Conf("slient") != "yes" { + cli.echo("\n") + cli.echo(cli.Conf("开场白")) + cli.echo("\n") + } + + cli.exit = make(chan bool) + cli.target = cli.Context + cli.history = make([]map[string]string, 0, 100) + cli.alias = make(map[string]string, 10) + + if f, e := os.Open(cli.Conf("init.sh")); e == nil { + cli.push(f) + } + + return true +} + +// }}} +func (cli *CLI) Start() bool { // {{{ + cli.Begin() + + go func() { + for cli.parse() { + } + }() + + for { + select { + case cli.Message = <-cli.Messages: + cli.deal(cli.Message) + if cli.Message.Wait != nil { + cli.Message.Wait <- true + } + case <-cli.exit: + return true + } + } + + return true +} + +// }}} +func (cli *CLI) Fork(c *ctx.Context, key string) ctx.Server { // {{{ + s := new(CLI) + s.Context = c + return s +} + +// }}} +func (cli *CLI) Spawn(c *ctx.Context, key string) ctx.Server { // {{{ + s := new(CLI) + s.Context = c + return s +} + +// }}} + +var Index = &ctx.Context{Name: "cli", Help: "命文", + Caches: map[string]*ctx.Cache{}, + Configs: map[string]*ctx.Config{ + "开场白": &ctx.Config{"开场白", "你好,命令行", "开场白", nil}, + "结束语": &ctx.Config{"结束语", "再见,命令行", "结束语", nil}, + "mode": &ctx.Config{"mode", "local", "命令执行模式", nil}, + "io": &ctx.Config{"io", "stdout", "输入输出", func(c *ctx.Context, arg string) string { + cli := c.Server.(*CLI) // {{{ + cli.out = os.Stdout + cli.push(os.Stdin) + + return arg + // }}} + }}, + "slient": &ctx.Config{"slient", "yes", "静默启动", nil}, + "init.sh": &ctx.Config{"init.sh", "etc/hi.sh", "启动脚本", nil}, + "default": &ctx.Config{"default", "get", "默认命令", nil}, + "log": &ctx.Config{"log", "var/bench.log", "日志文件", func(c *ctx.Context, arg string) string { + if l, e := os.Create(arg); e == nil { // {{{ + log.SetOutput(l) + } else { + log.Println("log", arg, "create error") + } + return arg + // }}} + }}, + "PS1": &ctx.Config{"PS1", "etcvpn>", "命令行提示符", func(c *ctx.Context, arg string) string { + cli := c.Server.(*CLI) + self := c.Server.(*CLI) // {{{ + if cli != nil && cli.target != nil { + arg = cli.target.Name + ">" + } + return fmt.Sprintf("%d[%s]\033[32m%s\033[0m ", self.index, time.Now().Format("15:04:05"), arg) + // }}} + }}, + }, + Commands: map[string]*ctx.Command{ + "cache": &ctx.Command{"cache [name [value [help]]]", "查看修改添加配置", func(c *ctx.Context, msg *ctx.Message, arg ...string) string { + cli := c.Server.(*CLI) // {{{ + + switch len(arg) { + case 1: + for k, v := range cli.target.Caches { + msg.Echo("%s(%s): %s\n", k, v.Value, v.Help) + } + case 2: + if v, ok := cli.target.Caches[arg[1]]; ok { + msg.Echo("%s: %s\n", v.Name, v.Help) + } + case 3: + switch arg[1] { + case "delete": + if _, ok := cli.target.Caches[arg[2]]; ok { + delete(cli.target.Caches, arg[2]) + } + default: + if _, ok := cli.target.Caches[arg[1]]; ok { + msg.Echo("%s: %s\n", arg[1], cli.target.Cap(arg[1:]...)) + } + } + case 5: + cli.target.Cap(arg[1:]...) + } + return "" + // }}} + }}, + "config": &ctx.Command{"config [name [value [help]]]", "查看修改添加配置", func(c *ctx.Context, msg *ctx.Message, arg ...string) string { + cli := c.Server.(*CLI) // {{{ + + switch len(arg) { + case 1: + for k, v := range cli.target.Configs { + msg.Echo("%s(%s): %s\n", k, v.Value, v.Help) + } + case 2: + if v, ok := cli.target.Configs[arg[1]]; ok { + msg.Echo("%s: %s\n", v.Name, v.Help) + } + case 3: + switch arg[1] { + case "delete": + if _, ok := cli.target.Configs[arg[2]]; ok { + delete(cli.target.Configs, arg[2]) + } + default: + if _, ok := cli.target.Configs[arg[1]]; ok { + cli.target.Conf(arg[1:]...) + } + } + case 5: + cli.target.Conf(arg[1:]...) + } + return "" + // }}} + }}, + "command": &ctx.Command{"command [name [value [help]]]", "查看修改添加配置", func(c *ctx.Context, msg *ctx.Message, arg ...string) string { + cli := c.Server.(*CLI) // {{{ + + switch len(arg) { + case 1: + for k, v := range cli.target.Commands { + msg.Echo("%s: %s\n", k, v.Help) + } + case 2: + if v, ok := cli.target.Commands[arg[1]]; ok { + msg.Echo("%s: %s\n", v.Name, v.Help) + } + case 3: + switch arg[1] { + case "delete": + if _, ok := cli.target.Commands[arg[2]]; ok { + delete(cli.target.Commands, arg[2]) + } + } + } + + msg.Echo("\n") + return "" + // }}} + }}, + "source": &ctx.Command{"source file", "运行脚本", func(c *ctx.Context, msg *ctx.Message, arg ...string) string { + cli := c.Server.(*CLI) // {{{ + + f, e := os.Open(arg[1]) + c.Check(e) + cli.push(f) + + return "" + // }}} + }}, + "alias": &ctx.Command{"alias [short [long]]", "查看日志", func(c *ctx.Context, msg *ctx.Message, arg ...string) string { + cli := c.Server.(*CLI) // {{{ + switch len(arg) { + case 1: + for k, v := range cli.alias { + msg.Echo("%s: %s\n", k, v) + } + case 3: + switch arg[1] { + case "delete": + delete(cli.alias, arg[2]) + default: + cli.alias[arg[1]] = arg[2] + msg.Echo("%s: %s\n", arg[1], cli.alias[arg[1]]) + } + } + return "" + // }}} + }}, + "history": &ctx.Command{"history number", "查看日志", func(c *ctx.Context, msg *ctx.Message, arg ...string) string { + cli := c.Server.(*CLI) // {{{ + switch len(arg) { + case 1: + for i, v := range cli.history { + msg.Echo("%d %s %s\n", i, v["time"], v["cli"]) + } + case 2: + n, e := strconv.Atoi(arg[1]) + if e == nil && 0 <= n && n < len(cli.history) { + return cli.history[n]["cli"] + } + } + return "" + // }}} + }}, + "message": &ctx.Command{"message [find|search name [switch]]|root|back|home", "查看上下文", func(c *ctx.Context, msg *ctx.Message, arg ...string) string { + return "" + }}, + "server": &ctx.Command{"server start|stop|switch", "服务启动停止切换", func(c *ctx.Context, msg *ctx.Message, arg ...string) string { + cli := c.Server.(*CLI) + switch len(arg) { + case 1: + go cli.target.Start() + case 2: + switch arg[1] { + case "start": + go cli.target.Start() + msg.Echo("\n") + case "stop": + case "switch": + } + } + return "" + }}, + "context": &ctx.Command{"context [find|search name [switch]]|root|back|home", "查看上下文", func(c *ctx.Context, msg *ctx.Message, arg ...string) string { + cli := c.Server.(*CLI) // {{{ + + switch len(arg) { + case 1: + cs := []*ctx.Context{cli.target} + for i := 0; i < len(cs); i++ { + if len(cs[i].Contexts) > 0 { + msg.Echo("%s: ", cs[i].Name) + for k, v := range cs[i].Contexts { + cs = append(cs, v) + msg.Echo("%s, ", k) + } + msg.Echo("\n") + } + } + case 2, 3, 4, 5: + switch arg[1] { + case "root": + cli.target = cli.Context.Root + case "back": + if cli.Context.Context != nil { + cli.target = cli.Context.Context + } + case "home": + cli.target = cli.Context + case "find": + cs := c.Root.Find(strings.Split(arg[2], ".")) + msg.Echo("%s: %s\n", cs.Name, cs.Help) + if len(arg) == 4 { + cli.target = cs + } + case "search": + cs := c.Root.Search(arg[2]) + for i, v := range cs { + msg.Echo("[%d] %s: %s\n", i, v.Name, v.Help) + } + + if len(arg) == 5 { + n, e := strconv.Atoi(arg[4]) + if 0 <= n && n < len(cs) && e == nil { + cli.target = cs[0] + } else { + msg.Echo("参数错误(0<=n<%s)", len(cs)) + } + } + + if len(arg) == 4 { + cli.target = cs[0] + } + default: + cs := c.Root.Find(strings.Split(arg[1], ".")) + msg.Echo("%s: %s\n", cs.Name, cs.Help) + if cs != nil { + cli.target = cs + } + } + } + msg.Echo("\ncurr: %s(%s)\n", cli.target.Name, cli.target.Help) + return "" + // }}} + }}, + }, + Messages: make(chan *ctx.Message, 10), +} + +func init() { + self := &CLI{} + self.Context = Index + ctx.Index.Register(Index, self) +} diff --git a/src/context/ctx.go b/src/context/ctx.go new file mode 100644 index 00000000..f8ce6eee --- /dev/null +++ b/src/context/ctx.go @@ -0,0 +1,538 @@ +package ctx // {{{ +// }}} +import ( // {{{ + "errors" + "fmt" + "log" + "runtime/debug" + "strconv" +) + +// }}} + +type Cache struct { // {{{ + Name string + Value string + Help string + Hand func(c *Context, arg string) string +} + +// }}} +type Config struct { // {{{ + Name string + Value string + Help string + Hand func(c *Context, arg string) string +} + +// }}} +type Command struct { // {{{ + Name string + Help string + Hand func(c *Context, m *Message, arg ...string) string +} + +// }}} +type Message struct { // {{{ + Meta map[string][]string + Data map[string]interface{} + Wait chan bool + + *Context +} + +func (m *Message) Add(key string, value ...string) string { // {{{ + if m.Meta == nil { + m.Meta = make(map[string][]string) + } + if _, ok := m.Meta[key]; !ok { + m.Meta[key] = []string{} + } + + m.Meta[key] = append(m.Meta[key], value...) + return value[0] +} + +// }}} +func (m *Message) Put(key string, value interface{}) interface{} { // {{{ + if m.Data == nil { + m.Data = make(map[string]interface{}) + } + + m.Data[key] = value + return value +} + +// }}} +func (m *Message) Has(key string) bool { // {{{ + if _, ok := m.Meta[key]; ok { + return true + } + if _, ok := m.Data[key]; ok { + return true + } + return false +} + +// }}} +func (m *Message) Get(key string) string { // {{{ + if meta, ok := m.Meta[key]; ok { + return meta[0] + } + return "" +} + +// }}} +func (m *Message) Echo(str string, arg ...interface{}) string { // {{{ + if m.Meta == nil { + m.Meta = make(map[string][]string) + } + if _, ok := m.Meta["result"]; !ok { + m.Meta["result"] = []string{} + } + + s := fmt.Sprintf(str, arg...) + m.Meta["result"] = append(m.Meta["result"], s) + return s +} + +// }}} +// }}} +type Server interface { // {{{ + Begin() bool + Start() bool + Fork(c *Context, key string) Server + Spawn(c *Context, key string) Server +} + +// }}} +type Context struct { // {{{ + Name string + Help string + + Caches map[string]*Cache + Configs map[string]*Config + Commands map[string]*Command + Messages chan *Message + Message *Message + Server + + Root *Context + Context *Context + Contexts map[string]*Context + + Index map[string]*Context + Shadows map[string]*Context +} + +func (c *Context) Check(e error) bool { // {{{ + if e != nil { + log.Println(c.Name, "error:", e) + debug.PrintStack() + panic(e) + } + return true +} + +// }}} +func (c *Context) Find(name []string) *Context { // {{{ + if x, ok := c.Contexts[name[0]]; ok { + if len(name) == 1 { + return x + } + return x.Find(name[1:]) + } + + return nil +} + +// }}} +func (c *Context) Search(name string) []*Context { // {{{ + ps := make([]*Context, 0, 3) + + cs := []*Context{c.Root} + for i := 0; i < len(cs); i++ { + for _, v := range cs[i].Contexts { + cs = append(cs, v) + } + + if cs[i].Name == name { + ps = append(ps, cs[i]) + } + } + + return ps +} + +// }}} +func (c *Context) Register(s *Context, self Server) bool { // {{{ + if c.Contexts == nil { + c.Contexts = make(map[string]*Context) + } + if x, ok := c.Contexts[s.Name]; ok { + panic(errors.New(x.Name + "上下文已存在")) + } + + c.Contexts[s.Name] = s + s.Root = c.Root + s.Context = c + s.Server = self + return true +} + +// }}} +func (c *Context) Init(arg ...string) { // {{{ + if c.Root == nil { + c.Root = c + cs := []*Context{c} + for i := 0; i < len(cs); i++ { + for _, v := range cs[i].Contexts { + cs = append(cs, v) + } + cs[i].Init() + } + + cs = c.Search(arg[0]) + cs[0].Begin() + cs[0].Start() + } else { + for _, v := range c.Contexts { + v.Root = c.Root + v.Context = c + } + } +} + +// }}} +func (c *Context) Fork(key string) { // {{{ + cs := []*Context{new(Context)} + *cs[0] = *c + + for i := 0; i < len(cs); i++ { + cs[i].Name = cs[i].Name + key + cs[i].Messages = make(chan *Message, len(cs[i].Messages)) + cs[i].Context.Register(cs[i], cs[i].Server.Fork(cs[i], key)) + + for _, v := range cs[i].Contexts { + s := new(Context) + *s = *v + s.Context = cs[i] + cs = append(cs, s) + } + } +} + +// }}} +func (c *Context) Spawn(key string) *Context { // {{{ + s := new(Context) + s.Name = c.Name + key + s.Help = c.Help + + s.Caches = make(map[string]*Cache) + s.Configs = make(map[string]*Config) + s.Commands = make(map[string]*Command) + s.Messages = make(chan *Message, len(c.Messages)) + c.Register(s, c.Server.Spawn(s, key)) + log.Println(c.Name, "spawn", c.Contexts[s.Name].Name) + return s +} + +// }}} + +func (c *Context) Cap(arg ...string) string { // {{{ + switch len(arg) { + case 1: + if v, ok := c.Caches[arg[0]]; ok { + if v.Hand != nil { + v.Value = v.Hand(c, v.Value) + } + log.Println(c.Name, "cache:", arg) + return v.Value + } + + if c.Context != nil { + return c.Context.Cap(arg...) + } + case 2: + if v, ok := c.Caches[arg[0]]; ok { + v.Value = arg[1] + if v.Hand != nil { + v.Value = v.Hand(c, v.Value) + } + return v.Value + } + + if c.Context != nil { + return c.Context.Cap(arg...) + } + case 3: + if v, ok := c.Caches[arg[0]]; ok { + panic(errors.New(v.Name + "缓存项已存在")) + } + + c.Caches[arg[0]] = &Cache{arg[0], arg[1], arg[2], nil} + log.Println(c.Name, "cache:", arg) + return arg[1] + default: + panic(errors.New(arg[0] + "缓存项参数错误")) + } + + panic(errors.New(arg[0] + "缓存项不存在")) +} + +// }}} +func (c *Context) Conf(arg ...string) string { // {{{ + switch len(arg) { + case 1: + if v, ok := c.Configs[arg[0]]; ok { + if v.Hand != nil { + return v.Hand(c, v.Value) + } + return v.Value + } + + if c.Context != nil { + return c.Context.Conf(arg...) + } + case 2: + if v, ok := c.Configs[arg[0]]; ok { + v.Value = arg[1] + if v.Hand != nil { + v.Hand(c, v.Value) + } + log.Println(c.Name, "config:", arg) + return v.Value + } + + if c.Context != nil { + return c.Context.Conf(arg...) + } + case 4: + if v, ok := c.Configs[arg[0]]; ok { + panic(errors.New(v.Name + "配置项已存在")) + } + + c.Configs[arg[0]] = &Config{arg[1], arg[2], arg[3], nil} + log.Println(c.Name, "config:", arg) + return arg[2] + default: + panic(errors.New(arg[0] + "配置项参数错误")) + } + + panic(errors.New(arg[0] + "配置项不存在")) +} + +// }}} +func (c *Context) Confi(arg ...string) int { // {{{ + n, e := strconv.Atoi(c.Conf(arg...)) + c.Check(e) + return n +} + +// }}} +func (c *Context) Cmd(m *Message, arg ...string) string { // {{{ + if x, ok := c.Commands[arg[0]]; ok { + log.Println(c.Name, "command:", arg) + return x.Hand(c, m, arg...) + } + + if c.Context != nil { + return c.Context.Cmd(m, arg...) + } + + panic(errors.New(fmt.Sprintf(arg[0] + "命令项不存在"))) +} + +// }}} +func (c *Context) Post(m *Message) bool { // {{{ + if c.Messages == nil { + c.Messages = make(chan *Message, 10) + } + + c.Messages <- m + if m.Wait != nil { + return <-m.Wait + } + log.Println(c.Context.Name, "message", m.Meta["detail"]) + return true +} + +// }}} + +func (c *Context) Add(arg ...string) { // {{{ + switch arg[0] { + case "context": + if len(arg) != 4 { + panic(errors.New("参数错误")) + } + if c.Index == nil { + panic(errors.New("索引表不存在")) + } + if v, ok := c.Index[arg[1]]; ok { + panic(errors.New(v.Name + "上下文已存在")) + } + + if c.Shadows == nil { + c.Shadows = make(map[string]*Context) + } + c.Shadows[arg[1]] = &Context{Name: arg[2], Help: arg[3], Index: c.Index} + c.Index[arg[1]] = c.Shadows[arg[1]] + log.Println(c.Name, "add context:", arg[1:]) + case "command": + if len(arg) != 3 { + panic(errors.New("参数错误")) + } + + if v, ok := c.Shadows[arg[1]]; ok { + if v.Commands == nil { + v.Commands = make(map[string]*Command) + } + if x, ok := v.Commands[arg[2]]; ok { + panic(errors.New(x.Name + "命令已存在")) + } + if x, ok := c.Commands[arg[2]]; ok { + log.Println(v.Name, "add command:", arg[2]) + v.Commands[arg[2]] = x + } else { + panic(errors.New(arg[2] + "命令不存在")) + } + } else { + panic(errors.New(arg[1] + "上下文不存在")) + } + case "config": + if len(arg) != 3 { + panic(errors.New("参数错误")) + } + + if v, ok := c.Shadows[arg[1]]; ok { + if v.Configs == nil { + v.Configs = make(map[string]*Config) + } + if x, ok := v.Configs[arg[2]]; ok { + panic(errors.New(x.Name + "配置项已存在")) + } + if x, ok := c.Configs[arg[2]]; ok { + log.Println(v.Name, "add config:", arg[2]) + v.Configs[arg[2]] = x + } else { + panic(errors.New(arg[2] + "配置项不存在")) + } + } else { + panic(errors.New(arg[1] + "上下文不存在")) + } + case "cache": + if len(arg) != 3 { + panic(errors.New("参数错误")) + } + + if v, ok := c.Shadows[arg[1]]; ok { + if v.Caches == nil { + v.Caches = make(map[string]*Cache) + } + if x, ok := v.Caches[arg[2]]; ok { + panic(errors.New(x.Name + "缓存项已存在")) + } + if x, ok := c.Caches[arg[2]]; ok { + log.Println(v.Name, "add cache:", arg[2]) + v.Caches[arg[2]] = x + } else { + panic(errors.New(arg[2] + "缓存项不存在")) + } + } else { + panic(errors.New(arg[1] + "上下文不存在")) + } + } +} + +// }}} +func (c *Context) Del(arg ...string) { // {{{ + cs := make([]*Context, 0, 5) + + switch arg[0] { + case "context": + if len(arg) != 2 { + panic(errors.New("参数错误")) + } + + if v, ok := c.Shadows[arg[1]]; ok { + cs = append(cs, v) + delete(c.Index, arg[1]) + delete(c.Shadows, arg[1]) + log.Println(c.Name, "del context:", arg[1]) + } + for i := 0; i < len(cs); i++ { + for k, v := range cs[i].Shadows { + cs = append(cs, v) + delete(c.Index, k) + log.Println(c.Name, "del context:", k) + } + } + case "command": + if len(arg) != 3 { + panic(errors.New("参数错误")) + } + + if v, ok := c.Shadows[arg[1]]; ok { + cs = append(cs, v) + delete(v.Commands, arg[2]) + log.Println(v.Name, "del command:", arg[2]) + } + for i := 0; i < len(cs); i++ { + for _, v := range cs[i].Shadows { + cs = append(cs, v) + delete(v.Commands, arg[2]) + log.Println(v.Name, "del command:", arg[2]) + } + } + case "config": + if len(arg) != 3 { + panic(errors.New("参数错误")) + } + + if v, ok := c.Shadows[arg[1]]; ok { + cs = append(cs, v) + delete(v.Configs, arg[2]) + log.Println(v.Name, "del config:", arg[2]) + } + for i := 0; i < len(cs); i++ { + for _, v := range cs[i].Shadows { + cs = append(cs, v) + delete(v.Configs, arg[2]) + log.Println(v.Name, "del config:", arg[2]) + } + } + case "cache": + if len(arg) != 3 { + panic(errors.New("参数错误")) + } + + if v, ok := c.Shadows[arg[1]]; ok { + cs = append(cs, v) + delete(v.Caches, arg[2]) + log.Println(v.Name, "del cache:", arg[2]) + } + for i := 0; i < len(cs); i++ { + for _, v := range cs[i].Shadows { + cs = append(cs, v) + delete(v.Caches, arg[2]) + log.Println(v.Name, "del cache:", arg[2]) + } + } + } +} + +// }}} +// }}} + +var Index = &Context{Name: "ctx", Help: "根文", + Caches: map[string]*Cache{}, + Configs: map[string]*Config{ + "开场白": &Config{"开场白", "你好,上下文", "开场白", nil}, + "结束语": &Config{"结束语", "再见,上下文", "结束语", nil}, + }, + Commands: map[string]*Command{}, +} + +func init() { + Index.Root = Index +} diff --git a/src/context/ctx_test.go b/src/context/ctx_test.go new file mode 100644 index 00000000..06d60305 --- /dev/null +++ b/src/context/ctx_test.go @@ -0,0 +1,115 @@ +package ctx + +import ( + "fmt" + "testing" +) + +func TestCtx(t *testing.T) { + context := Context{Name: "root", Help: "默认", + Caches: map[string]*Cache{ + "nclient": &Cache{"nclient", "10", "连接数量", func(c *Context) string { + return "10" + }}, + }, + Configs: map[string]*Config{ + "limit": &Config{"limit", "10", "最大连接数", func(c *Context, arg string) { + }}, + }, + Commands: map[string]*Command{ + "session": &Command{"session", "会话管理", func(c *Context, arg ...string) string { + return "ok" + }}, + }, + } + context.Index = map[string]*Context{"root": &context} + + ctx := context.Index["root"] + ctx.Add("context", "hi", "hi", "nice") + if _, ok := context.Contexts["hi"]; !ok { + t.Fatal("root.ctxs add error") + } + if c, ok := context.Index["hi"]; ok { + ctx.Add("command", "hi", "session") + if _, ok := c.Commands["session"]; ok { + if c.Cmd("session") != "ok" { + t.Fatal("hi.cmds.session: run error") + } + } else { + t.Fatal("hi.cmds: add error") + } + + ctx.Add("config", "hi", "limit") + ctx.Add("cache", "hi", "nclient") + + } else { + t.Fatal("root.index add error") + } + return + + if true { + ctx := context.Index["hi"] + if ctx.Cmd("session", "nice") == "ok" { + t.Log("hi.cmds.session: run") + } else { + t.Fatal("hi.cmds.session: run error") + } + t.Log() + + ctx.Add("context", "he", "he", "nice") + for k, v := range ctx.Contexts { + t.Log("hi.ctxs", k, v.Name, v.Help) + } + if len(ctx.Contexts) != 1 { + t.Fatal("hi.ctxs: add error") + } + for k, v := range ctx.Index { + t.Log("hi.index:", k, v.Name, v.Help) + } + if len(ctx.Index) != 3 { + t.Fatal("hi.index: add error") + } + t.Log() + + ctx.Add("command", "he", "session") + for k, v := range ctx.Contexts["he"].Commands { + t.Log("he.cmds:", k, v.Name, v.Help) + } + if len(ctx.Contexts["he"].Commands) != 1 { + t.Fatal("he.cmds: add error") + } + + } + + for k, v := range ctx.Index { + t.Log("root.index:", k, v.Name, v.Help) + } + if len(ctx.Index) != 3 { + t.Fatal("root.index add error") + } + t.Log() + + for k, v := range ctx.Index { + t.Log(fmt.Sprintf("root.index.%s.cmds: %d", k, len(v.Commands))) + } + t.Log() + ctx.Del("command", "hi", "session") + for k, v := range ctx.Index { + t.Log(fmt.Sprintf("root.index.%s.cmds: %d", k, len(v.Commands))) + } + t.Log() + + ctx.Del("context", "hi") + for k, v := range ctx.Contexts { + t.Log("root.ctxs:", k, v.Name, v.Help) + } + if len(ctx.Contexts) != 0 { + t.Fatal("root.ctxs: del error") + } + for k, v := range ctx.Index { + t.Log("root.index:", k, v.Name, v.Help) + } + if len(ctx.Index) != 1 { + t.Fatal("root.index del error") + } +} diff --git a/src/context/ssh/ssh.go b/src/context/ssh/ssh.go new file mode 100644 index 00000000..4d26f502 --- /dev/null +++ b/src/context/ssh/ssh.go @@ -0,0 +1,266 @@ +package ssh // {{{ +// }}} +import ( // {{{ + "bufio" + "context" + "crypto/tls" + "fmt" + "io" + "log" + "strings" +) + +// }}} + +type SSH struct { + w io.WriteCloser + bio *bufio.Reader + + meta map[string][]string + data []byte + head map[string][]string + body []string + len int + + *ctx.Context +} + +func (ssh *SSH) Scan() bool { // {{{ + if ssh.meta != nil { + return false + } + + meta := make(map[string][]string) + + for { + l, e := ssh.bio.ReadString('\n') + ssh.Check(e) + + log.Printf("%s >> %s", ssh.Name, l) + if len(l) == 1 { + break + } + + ls := strings.SplitN(l, ":", 2) + ls[0] = strings.TrimSpace(ls[0]) + ls[1] = strings.TrimSpace(ls[1]) + + if m, ok := meta[ls[0]]; ok { + meta[ls[0]] = append(m, ls[1]) + } else { + meta[ls[0]] = []string{ls[1]} + } + } + + ssh.meta = meta + return true +} + +// }}} +func (ssh *SSH) Has(field string) bool { // {{{ + _, ok := ssh.meta[field] + return ok +} + +// }}} +func (ssh *SSH) Get(field string) string { // {{{ + if m, ok := ssh.meta[field]; ok { + return m[0] + } + return "" +} + +// }}} + +func (ssh *SSH) Echo(name string, value interface{}) { // {{{ + fmt.Fprintln(ssh.w, name+":", value) + log.Println(ssh.Name, "<<", name+":", value) +} + +// }}} +func (ssh *SSH) Print(str string, arg ...interface{}) { // {{{ + if ssh.body == nil { + ssh.body = make([]string, 0, 3) + } + + s := fmt.Sprintf(str, arg...) + ssh.body = append(ssh.body, s) + ssh.len += len(s) +} + +// }}} + +func (ssh *SSH) End() { // {{{ + ssh.Echo("len", ssh.len) + fmt.Fprintln(ssh.w) + log.Println(ssh.Name, "<<") + + if ssh.len > 0 { + for i, v := range ssh.body { + n, e := fmt.Fprintf(ssh.w, v) + log.Println(ssh.Name, "<<", i, n, v) + ssh.Check(e) + } + } + + // ssh.Check(ssh.w.Flush()) + log.Println("\n") + + ssh.meta = nil + ssh.body = nil + ssh.len = 0 +} + +// }}} + +func (ssh *SSH) Begin() bool { // {{{ + // ssh.Conf("log", ssh.Conf("log")) + for k, v := range ssh.Configs { + ssh.Conf(k, v.Value) + } + + return true +} + +// }}} +func (ssh *SSH) Start() bool { // {{{ + ssh.Begin() + + if ssh.Cap("status") == "start" { + return false + } + + defer ssh.Cap("status", "stop") + + if ssh.Conf("client") == "yes" { + conn, e := tls.Dial("tcp", ssh.Conf("address"), &tls.Config{InsecureSkipVerify: true}) + ssh.Check(e) + ssh.w = conn + ssh.bio = bufio.NewReader(conn) + log.Println(ssh.Name, "->", conn.RemoteAddr(), "connect") + ssh.Cap("status", "start") + + ssh.Echo("master", ssh.Conf("master")) + ssh.End() + + ssh.Scan() + if ssh.Get("master") == "yes" { + ssh.Conf("master", "no") + } else { + ssh.Conf("master", "yes") + } + + log.Println("fuck") + ssh.End() + + if ssh.Conf("master") == "yes" { + } else { + for ssh.Scan() { + m := &ctx.Message{Meta: ssh.meta} + m.Context = ssh.Context + ssh.Root.Find([]string{"cli"}).Post(m) + ssh.End() + } + } + + } else { + pair, e := tls.LoadX509KeyPair(ssh.Conf("cert"), ssh.Conf("key")) + ssh.Check(e) + + ls, e := tls.Listen("tcp", ssh.Conf("address"), &tls.Config{Certificates: []tls.Certificate{pair}}) + ssh.Check(e) + ssh.Cap("status", "start") + for { + conn, e := ls.Accept() + ssh.Check(e) + log.Println(ssh.Name, "<-", conn.RemoteAddr(), "connect") + + go func() { + s := ssh.Context.Spawn(conn.RemoteAddr().String()) + psh := s.Server.(*SSH) + psh.w = conn + psh.bio = bufio.NewReader(conn) + + psh.Scan() + if psh.Get("master") == "yes" { + psh.Conf("master", "master", "no", "主控或被控") + } else { + psh.Conf("master", "master", "yes", "主控或被控") + } + + psh.Echo("master", psh.Conf("master")) + psh.End() + + psh.Scan() + + if psh.Conf("master") == "yes" { + } else { + psh.meta = nil + for psh.Scan() { + m := &ctx.Message{Meta: psh.meta, Wait: make(chan bool)} + psh.Root.Find([]string{"cli"}).Post(m) + for _, v := range m.Meta["result"] { + psh.Echo("result", v) + } + + psh.End() + } + } + }() + } + } + return true +} + +// }}} +func (ssh *SSH) Fork(c *ctx.Context, key string) ctx.Server { // {{{ + s := new(SSH) + s.Context = c + return s +} + +// }}} +func (ssh *SSH) Spawn(c *ctx.Context, key string) ctx.Server { // {{{ + s := new(SSH) + s.Context = c + return s +} + +// }}} + +var Index = &ctx.Context{Name: "ssh", Help: "运程连接", + Caches: map[string]*ctx.Cache{ + "status": &ctx.Cache{"status", "stop", "服务器状态", nil}, + }, + Configs: map[string]*ctx.Config{ + "master": &ctx.Config{"master", "yes", "主控或被控", nil}, + "client": &ctx.Config{"client", "yes", "连接或监听", nil}, + "address": &ctx.Config{"address", ":9090", "连接或监听的地址", nil}, + "cert": &ctx.Config{"cert", "etc/cert.pem", "证书文件", nil}, + "key": &ctx.Config{"key", "etc/key.pem", "私钥文件", nil}, + }, + Commands: map[string]*ctx.Command{ + "remote": &ctx.Command{"remote", "远程命令", func(c *ctx.Context, msg *ctx.Message, arg ...string) string { + ssh := c.Server.(*SSH) // {{{ + if c.Conf("master") == "yes" { + for _, v := range arg[1:] { + ssh.Echo("detail", v) + } + ssh.End() + } + ssh.Scan() + for _, v := range ssh.meta["result"] { + msg.Echo(v) + } + msg.Echo("\n") + return "" + // }}} + }}, + }, +} + +func init() { + ssh := &SSH{} + ssh.Context = Index + ctx.Index.Register(Index, ssh) +}