diff --git a/base/tcp/server.go b/base/tcp/server.go index c2d2dae2..ca3e495d 100644 --- a/base/tcp/server.go +++ b/base/tcp/server.go @@ -68,6 +68,7 @@ const ( ) const ( LISTEN = "listen" + UNIX = "unix" ) const SERVER = "server" diff --git a/base/web/option.go b/base/web/option.go index 8f132e54..e821c112 100644 --- a/base/web/option.go +++ b/base/web/option.go @@ -129,7 +129,7 @@ func ToastProcess(m *ice.Message, arg ...ice.Any) func() { Toast(m, toastContent(m, ice.PROCESS), arg...) return func() { Toast(m, toastContent(m, ice.SUCCESS)) } } -func GoToast(m *ice.Message, title string, cb func(toast func(string, int, int)) []string) { +func GoToast(m *ice.Message, title string, cb func(toast func(string, int, int)) []string) *ice.Message { _total := 0 toast := func(name string, count, total int) { kit.If(total == 0, func() { total = 1 }) @@ -144,4 +144,5 @@ func GoToast(m *ice.Message, title string, cb func(toast func(string, int, int)) } else { toast(ice.SUCCESS, _total, _total) } + return m } diff --git a/core/code/xterm.go b/core/code/xterm.go index 85cb10db..ab294a62 100644 --- a/core/code/xterm.go +++ b/core/code/xterm.go @@ -41,7 +41,7 @@ func _xterm_get(m *ice.Message, h string) xterm.XTerm { for { if n, e := term.Read(buf); !m.Warn(e) && e == nil { if _xterm_echo(m, h, string(buf[:n])); len(text) > 0 { - kit.If(text[0], func(cmd string) { m.Go(func() { m.Sleep30ms(); term.Writeln(cmd) }) }) + kit.If(text[0], func(cmd string) { m.Go(func() { m.Sleep30ms(); term.Write([]byte(cmd + lex.NL)) }) }) text = text[1:] } } else { @@ -54,11 +54,12 @@ func _xterm_get(m *ice.Message, h string) xterm.XTerm { }).(xterm.XTerm) } func _xterm_echo(m *ice.Message, h string, str string) { - m.Options(ice.MSG_COUNT, "0", ice.LOG_DISABLE, ice.TRUE, "__target", "", ice.MSG_DAEMON, mdb.HashSelectField(m, h, cli.DAEMON)) + m.Options(ice.MSG_DAEMON, mdb.HashSelectField(m, h, cli.DAEMON), ice.MSG_COUNT, "0", "__target", "") + m.Options(ice.LOG_DISABLE, ice.TRUE) web.PushNoticeGrow(m, h, str) } func _xterm_cmds(m *ice.Message, h string, cmd string, arg ...ice.Any) { - kit.If(cmd != "", func() { _xterm_get(m, h).Writeln(cmd, arg...) }) + kit.If(cmd != "", func() { _xterm_get(m, h).Write([]byte(kit.Format(cmd, arg...) + lex.NL)) }) m.ProcessHold() } diff --git a/exec.go b/exec.go index 575630c1..8567530e 100644 --- a/exec.go +++ b/exec.go @@ -65,6 +65,11 @@ func (m *Message) Go(cb func(), arg ...Any) *Message { task.Put(m.FormatTaskMeta(), arg[0], func(task *task.Task) { m.TryCatch(true, func(m *Message) { cb() }) }) return m } +func (m *Message) GoWait(cb func(func()), arg ...Any) *Message { + res := make(chan bool, 2) + defer func() { <-res }() + return m.Go(func() { cb(func() { res <- true }) }, arg...) +} func (m *Message) Wait(d string, cb ...Handler) (wait func() bool, done Handler) { sync := make(chan bool, 2) t := time.AfterFunc(kit.Duration(d), func() { sync <- false }) diff --git a/misc/misc.shy b/misc/misc.shy index 34af7de9..17849538 100644 --- a/misc/misc.shy +++ b/misc/misc.shy @@ -4,9 +4,9 @@ websocket webview qrcode xterm - git ssh + vim bash tmux diff --git a/misc/ssh/connect.go b/misc/ssh/connect.go index 71639e18..9cb53dba 100644 --- a/misc/ssh/connect.go +++ b/misc/ssh/connect.go @@ -20,6 +20,9 @@ import ( "shylinux.com/x/icebergs/base/nfs" psh "shylinux.com/x/icebergs/base/ssh" "shylinux.com/x/icebergs/base/tcp" + "shylinux.com/x/icebergs/base/web" + "shylinux.com/x/icebergs/core/code" + "shylinux.com/x/icebergs/misc/xterm" kit "shylinux.com/x/toolkits" ) @@ -30,26 +33,26 @@ func _ssh_open(m *ice.Message, arg ...string) { defer terminal.Restore(fd, oldState) } w, h, _ := terminal.GetSize(fd) - c.Write([]byte(fmt.Sprintf("#height:%d,width:%d\n", h, w))) - for _, item := range kit.Simple(m.Optionv(ice.INIT)) { + c.Write([]byte(fmt.Sprintf("#height:%d,width:%d"+lex.NL, h, w))) + kit.For(kit.Simple(m.Optionv(ice.INIT)), func(cmd string) { + defer c.Write([]byte(cmd + lex.NL)) m.Sleep300ms() - c.Write([]byte(item + lex.NL)) - } - m.Go(func() { io.Copy(c, os.Stdin) }) - io.Copy(os.Stdout, c) + }) + m.Go(func() { io.Copy(os.Stdout, c) }) + io.Copy(c, os.Stdin) }, arg...) } func _ssh_dial(m *ice.Message, cb func(net.Conn), arg ...string) { p := kit.HomePath(".ssh", fmt.Sprintf("%s@%s:%s", m.Option(aaa.USERNAME), m.Option(tcp.HOST), m.Option(tcp.PORT))) if nfs.Exists(m, p) { - if c, e := net.Dial("unix", p); e == nil { + if c, e := net.Dial(tcp.UNIX, p); e == nil { cb(c) return } nfs.Remove(m, p) } _ssh_conn(m, func(client *ssh.Client) { - if l, e := net.Listen("unix", p); !m.Warn(e, ice.ErrNotValid) { + if l, e := net.Listen(tcp.UNIX, p); !m.Warn(e, ice.ErrNotValid) { defer func() { nfs.Remove(m, p) }() defer l.Close() m.Go(func() { @@ -72,18 +75,15 @@ func _ssh_dial(m *ice.Message, cb func(net.Conn), arg ...string) { } s.Stdin, s.Stdout, s.Stderr = c, c, c s.RequestPty(kit.Env(cli.TERM), h, w, ssh.TerminalModes{ssh.ECHO: 1, ssh.TTY_OP_ISPEED: 14400, ssh.TTY_OP_OSPEED: 14400}) + gdb.SignalNotify(m, 28, func() { w, h, _ := terminal.GetSize(int(os.Stdin.Fd())); s.WindowChange(h, w) }) defer s.Wait() - gdb.SignalNotify(m, 28, func() { - w, h, _ := terminal.GetSize(int(os.Stdin.Fd())) - s.WindowChange(h, w) - }) s.Shell() }) }(c) } }) } - if c, e := net.Dial("unix", p); e == nil { + if c, e := net.Dial(tcp.UNIX, p); !m.Warn(e) { cb(c) } }, arg...) @@ -109,7 +109,6 @@ func _ssh_conn(m *ice.Message, cb func(*ssh.Client), arg ...string) { } case strings.HasSuffix(p, "password:"): res = append(res, m.Option(aaa.PASSWORD)) - default: } } return @@ -124,54 +123,108 @@ func _ssh_conn(m *ice.Message, cb func(*ssh.Client), arg ...string) { } }) } +func _ssh_hold(m *ice.Message, c *ssh.Client) { + if s, e := _ssh_session(m, c); !m.Warn(e, ice.ErrNotValid) { + defer s.Wait() + s.Shell() + } +} +func _ssh_target(m *ice.Message, name string) *ssh.Client { + return mdb.HashSelectTarget(m, name, func(value ice.Maps) (res ice.Any) { + m.GoWait(func(done func()) { + _ssh_conn(m.Spawn(value), func(c *ssh.Client) { + defer _ssh_hold(m, c) + defer done() + res = c + }) + }) + return + }).(*ssh.Client) +} const SSH = "ssh" +const ( + DIRECT = "direct" +) const CONNECT = "connect" func init() { psh.Index.MergeCommands(ice.Commands{ - CONNECT: {Name: "connect name auto", Help: "连接", Actions: ice.MergeActions(ice.Actions{ - ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) { - mdb.HashSelect(m).Table(func(value ice.Maps) { - if value[mdb.STATUS] == tcp.OPEN { - m.Cmd("", tcp.DIAL, mdb.NAME, value[mdb.NAME], value) - } - }) - }}, + CONNECT: {Help: "连接", Actions: ice.MergeActions(ice.Actions{ tcp.OPEN: {Name: "open authfile username=shy password verfiy host=shylinux.com port=22 private=.ssh/id_rsa", Help: "终端", Hand: func(m *ice.Message, arg ...string) { defer nfs.OptionLoad(m, m.Option("authfile")).Echo("exit %s@%s:%s\n", m.Option(aaa.USERNAME), m.Option(tcp.HOST), m.Option(tcp.PORT)) _ssh_open(m, arg...) }}, tcp.DIAL: {Name: "dial name=shylinux host=shylinux.com port=22 username=shy private=.ssh/id_rsa", Help: "添加", Hand: func(m *ice.Message, arg ...string) { m.Go(func() { - _ssh_conn(m, func(client *ssh.Client) { - mdb.HashCreate(m.Spawn(), m.OptionSimple(mdb.NAME, tcp.HOST, tcp.PORT, aaa.USERNAME), mdb.STATUS, tcp.OPEN, kit.Dict(mdb.TARGET, client)) - m.Cmd("", SESSION, m.OptionSimple(mdb.NAME)) + msg := m.Spawn() + _ssh_conn(m, func(c *ssh.Client) { + defer _ssh_hold(m, c) + mdb.HashCreate(msg, m.OptionSimple(mdb.NAME, tcp.HOST, tcp.PORT, aaa.USERNAME, PRIVATE), kit.Dict(mdb.TARGET, c)) }, arg...) - }) - m.Sleep300ms() + }).Sleep3s() }}, - SESSION: {Help: "会话", Hand: func(m *ice.Message, arg ...string) { - if c, e := _ssh_session(m, mdb.HashSelectTarget(m, m.Option(mdb.NAME), nil).(*ssh.Client)); !m.Warn(e, ice.ErrNotValid) { - defer c.Wait() - c.Shell() - } - }}, - ctx.COMMAND: {Name: "command cmd=pwd", Help: "命令", Hand: func(m *ice.Message, arg ...string) { - client := mdb.HashSelectTarget(m, m.Option(mdb.NAME), nil).(*ssh.Client) - if s, e := client.NewSession(); !m.Warn(e, ice.ErrNotValid) { + SESSION: {Help: "会话", Hand: func(m *ice.Message, arg ...string) { _ssh_hold(m, _ssh_target(m, m.Option(mdb.NAME))) }}, + DIRECT: {Name: "direct cmd=pwd", Help: "命令", Hand: func(m *ice.Message, arg ...string) { + if m.Option(mdb.NAME) == "" { + msg := m.Cmds("") + web.GoToast(m, m.Option(ice.CMD), func(toast func(string, int, int)) []string { + count, total := 0, msg.Length() + toast("", count, total) + msg.Table(func(value ice.Maps) { + toast(value[mdb.NAME], count, total) + msg := m.Cmds("", m.ActionKey(), value) + kit.If(len(msg.Resultv()) == 0, func() { msg.TableEcho() }) + m.Push(mdb.TIME, msg.Time()) + m.Push(mdb.NAME, value[mdb.NAME]) + m.Push(cli.COST, m.FormatCost()) + m.Push(RES, msg.Result()) + count++ + }) + return nil + }).ProcessInner() + } else if s, e := _ssh_target(m, m.Option(mdb.NAME)).NewSession(); !m.Warn(e, ice.ErrNotValid) { defer s.Close() if b, e := s.CombinedOutput(m.Option(ice.CMD)); !m.Warn(e, ice.ErrNotValid) { - m.Echo(string(b)) + m.Echo(string(b)).ProcessInner() } + } else { + mdb.HashSelectUpdate(m, m.Option(mdb.NAME), func(value ice.Map) { delete(value, mdb.TARGET) }) } }}, - }, mdb.StatusHashAction(mdb.SHORT, mdb.NAME, mdb.FIELD, "time,name,status,username,host,port")), Hand: func(m *ice.Message, arg ...string) { - if mdb.HashSelect(m, arg...).Table(func(value ice.Maps) { - m.PushButton(kit.Select("", "command,session", value[mdb.STATUS] == tcp.OPEN), mdb.REMOVE) - }); len(arg) == 0 { - m.Action(tcp.DIAL) + code.XTERM: {Hand: func(m *ice.Message, arg ...string) { + ctx.Process(m, code.XTERM, []string{SSH + lex.SP + m.Option(mdb.NAME)}, arg...) + }}, + }, mdb.StatusHashAction(mdb.SHORT, mdb.NAME, mdb.FIELD, "time,name,username,private,host,port"), mdb.ImportantHashAction()), Hand: func(m *ice.Message, arg ...string) { + if mdb.HashSelect(m, arg...).PushAction(code.XTERM, DIRECT, SESSION, mdb.REMOVE); len(arg) == 0 { + m.Sort(mdb.NAME).Action(tcp.DIAL, DIRECT) } }}, }) } + +type session struct { + name string + sess *ssh.Session + pty *os.File +} + +func NewSession(m *ice.Message, arg ...string) (xterm.XTerm, error) { + sess := &session{name: arg[0]} + m.GoWait(func(done func()) { + m.Cmd("ssh.connect", SESSION, kit.Dict(mdb.NAME, arg[0]), func(s *ssh.Session) { + defer done() + pty, tty, _ := xterm.Open() + sess.sess, sess.pty = s, pty + s.Stdin, s.Stdout, s.Stderr = tty, tty, tty + s.RequestPty(kit.Env(cli.TERM), 24, 80, ssh.TerminalModes{ssh.ECHO: 0, ssh.TTY_OP_ISPEED: 14400, ssh.TTY_OP_OSPEED: 14400}) + }) + }) + return sess, nil +} +func (s session) Setsize(h, w string) error { return s.sess.WindowChange(kit.Int(h), kit.Int(w)) } +func (s session) Write(buf []byte) (int, error) { return s.pty.Write(buf) } +func (s session) Read(buf []byte) (int, error) { return s.pty.Read(buf) } +func (s session) Close() error { return s.sess.Close() } + +func init() { xterm.AddCommand(SSH, NewSession) } diff --git a/misc/ssh/rsa.go b/misc/ssh/rsa.go index 62f8c1a5..ab3901b2 100644 --- a/misc/ssh/rsa.go +++ b/misc/ssh/rsa.go @@ -1,14 +1,12 @@ package ssh import ( - "crypto" "crypto/rand" "crypto/rsa" - "crypto/sha256" "crypto/x509" - "encoding/hex" "encoding/pem" "path" + "strings" "golang.org/x/crypto/ssh" @@ -21,10 +19,8 @@ import ( ) const ( - PUBLIC = "public" PRIVATE = "private" - VERIFY = "verify" - SIGN = "sign" + PUBLIC = "public" ) const RSA = "rsa" @@ -46,7 +42,8 @@ func init() { mdb.CREATE: {Name: "create bits=2048,4096 title=some", Hand: func(m *ice.Message, arg ...string) { if key, err := rsa.GenerateKey(rand.Reader, kit.Int(m.Option(BITS))); !m.Warn(err, ice.ErrNotValid) { if pub, err := ssh.NewPublicKey(key.Public()); !m.Warn(err, ice.ErrNotValid) { - mdb.HashCreate(m, m.OptionSimple(TITLE), PUBLIC, string(ssh.MarshalAuthorizedKey(pub))+lex.SP+m.Option(TITLE), + mdb.HashCreate(m, m.OptionSimple(TITLE), + PUBLIC, strings.TrimSpace(string(ssh.MarshalAuthorizedKey(pub)))+lex.SP+strings.TrimSpace(m.Option(TITLE)), PRIVATE, string(pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})), ) } @@ -65,46 +62,6 @@ func init() { PUBLIC, m.Cmdx(nfs.CAT, kit.HomePath(m.Option(PUB))), )) }}, - SIGN: {Hand: func(m *ice.Message, arg ...string) { - if !nfs.Exists(m, "etc/id_rsa") { - if key, err := rsa.GenerateKey(rand.Reader, kit.Int("2048")); !m.Warn(err, ice.ErrNotValid) { - m.Cmd(nfs.SAVE, "etc/id_rsa", string(pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}))) - m.Cmd(nfs.SAVE, "etc/id_rsa.pub", string(pem.EncodeToMemory(&pem.Block{Type: "RSA PUBLIC KEY", Bytes: x509.MarshalPKCS1PublicKey(key.Public().(*rsa.PublicKey))}))) - } - } - block, _ := pem.Decode([]byte(m.Cmdx(nfs.CAT, "etc/id_rsa"))) - key, err := x509.ParsePKCS1PrivateKey(block.Bytes) - if m.Warn(err) { - return - } - hash := sha256.New() - if _, err := hash.Write([]byte(arg[0])); m.Warn(err) { - return - } - signature, err := rsa.SignPSS(rand.Reader, key, crypto.SHA256, hash.Sum(nil), nil) - if m.Warn(err) { - return - } - m.Echo(hex.EncodeToString(signature)) - }}, - VERIFY: {Hand: func(m *ice.Message, arg ...string) { - block, _ := pem.Decode([]byte(m.Cmdx(nfs.CAT, "etc/id_rsa.pub"))) - pub, err := x509.ParsePKCS1PublicKey(block.Bytes) - if m.Warn(err) { - return - } - signature, err := hex.DecodeString(arg[1]) - if m.Warn(err) { - return - } - hash := sha256.New() - if _, err := hash.Write([]byte(arg[0])); m.Warn(err) { - return - } - if !m.Warn(rsa.VerifyPSS(pub, crypto.SHA256, hash.Sum(nil), signature, nil)) { - m.Echo(ice.OK) - } - }}, }, mdb.HashAction(mdb.SHORT, PRIVATE, mdb.FIELD, "time,hash,title,public,private")), Hand: func(m *ice.Message, arg ...string) { if mdb.HashSelect(m, arg...).PushAction(mdb.EXPORT, mdb.REMOVE); len(arg) == 0 { m.Action(mdb.CREATE, mdb.IMPORT) diff --git a/misc/ssh/service.go b/misc/ssh/service.go index 6401e898..ff6878f9 100644 --- a/misc/ssh/service.go +++ b/misc/ssh/service.go @@ -145,7 +145,7 @@ const SERVICE = "service" func init() { psh.Index.MergeCommands(ice.Commands{ - SERVICE: {Name: "service port id auto listen prunes", Icon: "ssh.png", Help: "服务", Actions: ice.MergeActions(ice.Actions{ + SERVICE: {Name: "service port id auto", Icon: "ssh.png", Help: "服务", Actions: ice.MergeActions(ice.Actions{ ice.CTX_INIT: {Hand: func(m *ice.Message, arg ...string) { mdb.HashSelect(m).Table(func(value ice.Maps) { if value[mdb.STATUS] == tcp.OPEN { @@ -190,16 +190,14 @@ func init() { }}, aaa.INVITE: {Help: "邀请", Hand: func(m *ice.Message, arg ...string) { m.Option(cli.HOSTNAME, tcp.PublishLocalhost(m, web.UserWeb(m).Hostname())) - if buf, err := kit.Render(`ssh -p {{.Option "port"}} {{.Option "user.name"}}@{{.Option "hostname"}}`, m); err == nil { - m.EchoScript(string(buf)) - } + m.EchoScript(kit.Renders(`ssh -p {{.Option "port"}} {{.Option "user.name"}}@{{.Option "hostname"}}`, m)) }}, }, mdb.StatusHashAction( mdb.SHORT, tcp.PORT, mdb.FIELD, "time,port,status,private,authkey,count", mdb.FIELDS, "time,id,type,name,text", WELCOME, "welcome to contexts world\r\n", GOODBYE, "goodbye of contexts world\r\n", )), Hand: func(m *ice.Message, arg ...string) { - if mdb.ZoneSelect(m, arg...); len(arg) == 0 { - m.PushAction(aaa.INVITE, mdb.INSERT, ctx.LOAD, ctx.SAVE, mdb.REMOVE) + if mdb.ZoneSelect(m, arg...).PushAction(aaa.INVITE, mdb.INSERT, ctx.LOAD, ctx.SAVE, mdb.REMOVE); len(arg) == 0 { + m.Action(tcp.LISTEN) } }}, }) diff --git a/misc/ssh/session.go b/misc/ssh/session.go index c4af5cb4..d0085ea9 100644 --- a/misc/ssh/session.go +++ b/misc/ssh/session.go @@ -11,16 +11,27 @@ import ( "shylinux.com/x/icebergs/base/mdb" psh "shylinux.com/x/icebergs/base/ssh" "shylinux.com/x/icebergs/base/tcp" + "shylinux.com/x/icebergs/core/code" kit "shylinux.com/x/toolkits" ) -func _ssh_session(m *ice.Message, client *ssh.Client) (*ssh.Session, error) { - s, e := client.NewSession() +func _ssh_session(m *ice.Message, c *ssh.Client) (*ssh.Session, error) { + s, e := c.NewSession() m.Assert(e) + switch cb := m.OptionCB("").(type) { + case func(s *ssh.Session): + cb(s) + return s, nil + } out, e := s.StdoutPipe() m.Assert(e) in, e := s.StdinPipe() m.Assert(e) + switch cb := m.OptionCB("").(type) { + case func(s *ssh.Session, in io.Writer, out io.Reader): + cb(s, in, out) + return s, nil + } h := m.Cmdx(SESSION, mdb.CREATE, mdb.STATUS, tcp.OPEN, CONNECT, m.Option(mdb.NAME), kit.Dict(mdb.TARGET, in)) m.Go(func() { buf := make([]byte, ice.MOD_BUFS) @@ -28,7 +39,7 @@ func _ssh_session(m *ice.Message, client *ssh.Client) (*ssh.Session, error) { if n, e := out.Read(buf); e != nil { break } else { - m.Cmd(SESSION, mdb.INSERT, mdb.HASH, h, mdb.TYPE, RES, mdb.TEXT, string(buf[:n])) + m.Cmd(SESSION, mdb.INSERT, mdb.ZONE, h, mdb.TYPE, RES, mdb.TEXT, string(buf[:n])) } } }) @@ -51,7 +62,7 @@ const SESSION = "session" func init() { psh.Index.MergeCommands(ice.Commands{ - SESSION: {Name: "session hash id auto", Help: "会话", Actions: ice.MergeActions(ice.Actions{ + SESSION: {Help: "会话", Actions: ice.MergeActions(ice.Actions{ mdb.REPEAT: {Help: "执行", Hand: func(m *ice.Message, arg ...string) { m.Cmdy("", ctx.ACTION, ctx.COMMAND, CMD, m.Option(mdb.TEXT)) }}, ctx.COMMAND: {Name: "command cmd=pwd", Help: "命令", Hand: func(m *ice.Message, arg ...string) { mdb.ZoneInsert(m, m.OptionSimple(mdb.HASH), mdb.TYPE, CMD, mdb.TEXT, m.Option(CMD)) @@ -59,7 +70,9 @@ func init() { w.Write([]byte(m.Option(CMD) + lex.NL)) m.Sleep300ms() } + m.ProcessRefresh() }}, + code.XTERM: {}, }, mdb.PageZoneAction(mdb.SHORT, mdb.UNIQ, mdb.FIELD, "time,hash,count,status,connect", mdb.FIELDS, "time,id,type,text")), Hand: func(m *ice.Message, arg ...string) { if mdb.PageZoneSelect(m, arg...); len(arg) == 0 { m.Table(func(value ice.Maps) { diff --git a/misc/xterm/iterm.go b/misc/xterm/iterm.go index 338d561e..c7053ff4 100644 --- a/misc/xterm/iterm.go +++ b/misc/xterm/iterm.go @@ -35,7 +35,8 @@ type iterm struct { *idata } -func NewITerm(m *ice.Message) (XTerm, error) { +func init() { AddCommand("ish", NewITerm) } +func NewITerm(m *ice.Message, arg ...string) (XTerm, error) { r, w, e := os.Pipe() return &iterm{m: m, r: r, w: w, idata: &idata{cmds: kit.Simple( kit.SortedKey(ice.Info.Index), diff --git a/misc/xterm/xterm.go b/misc/xterm/xterm.go index 0f0be2bd..b143897f 100644 --- a/misc/xterm/xterm.go +++ b/misc/xterm/xterm.go @@ -20,7 +20,6 @@ type Winsize struct { type XTerm interface { Setsize(rows, cols string) error - Writeln(data string, arg ...ice.Any) Write(buf []byte) (int, error) Read(buf []byte) (int, error) Close() error @@ -38,11 +37,16 @@ func (s xterm) Write(buf []byte) (int, error) { return s.File.Write(buf) } func (s xterm) Read(buf []byte) (int, error) { return s.File.Read(buf) } func (s xterm) Close() error { return s.Cmd.Process.Kill() } +type handler func(m *ice.Message, arg ...string) (XTerm, error) + +var list = map[string]handler{} + +func AddCommand(key string, cb handler) { list[key] = cb } + func Command(m *ice.Message, dir string, cli string, arg ...string) (XTerm, error) { - if path.Base(cli) == "ish" { - return NewITerm(m) + if cb, ok := list[path.Base(cli)]; ok { + return cb(m.Spawn(), arg...) } - m.Debug("command %v %v", cli, arg) cmd := exec.Command(cli, arg...) cmd.Dir = nfs.MkdirAll(m, kit.Path(dir)) cmd.Env = append(cmd.Env, os.Environ()...)