forked from x/icebergs
96 lines
2.6 KiB
Go
96 lines
2.6 KiB
Go
package xterm
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
|
|
ice "shylinux.com/x/icebergs"
|
|
"shylinux.com/x/icebergs/base/cli"
|
|
"shylinux.com/x/icebergs/base/lex"
|
|
"shylinux.com/x/icebergs/base/nfs"
|
|
kit "shylinux.com/x/toolkits"
|
|
"shylinux.com/x/toolkits/task"
|
|
)
|
|
|
|
type Winsize struct {
|
|
Rows uint16 // ws_row: Number of rows (in cells)
|
|
Cols uint16 // ws_col: Number of columns (in cells)
|
|
X uint16 // ws_xpixel: Width in pixels
|
|
Y uint16 // ws_ypixel: Height in pixels
|
|
}
|
|
|
|
type XTerm interface {
|
|
Setsize(rows, cols string) error
|
|
Write(buf []byte) (int, error)
|
|
Read(buf []byte) (int, error)
|
|
Close() error
|
|
}
|
|
type xterm struct {
|
|
*exec.Cmd
|
|
*os.File
|
|
}
|
|
|
|
func (s xterm) Setsize(rows, cols string) error {
|
|
return Setsize(s.File, &Winsize{Rows: uint16(kit.Int(rows)), Cols: uint16(kit.Int(cols))})
|
|
}
|
|
func (s xterm) Writeln(str string, arg ...ice.Any) { s.Write([]byte(kit.Format(str, arg...) + lex.NL)) }
|
|
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 { s.Cmd.Process.Kill(); return s.File.Close() }
|
|
|
|
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, cmd string, arg ...string) (XTerm, error) {
|
|
if cb, ok := list[path.Base(cmd)]; ok {
|
|
m.Debug("find shell %s %s", cmd, kit.FileLines(cb))
|
|
return cb(m.Spawn(), arg...)
|
|
}
|
|
if m.Cmd(cli.SUDO, cmd).Length() > 0 {
|
|
m.Debug("find sudo %s", cmd)
|
|
cmd, arg = cli.SUDO, kit.Simple(cmd, arg)
|
|
}
|
|
p := exec.Command(cmd, arg...)
|
|
p.Dir = nfs.MkdirAll(m, kit.Path(dir))
|
|
p.Env = append(p.Env, os.Environ()...)
|
|
p.Env = append(p.Env, "TERM=xterm")
|
|
if pty, tty, err := Open(); err != nil {
|
|
return nil, err
|
|
} else {
|
|
Setsid(p)
|
|
p.Stdin, p.Stdout, p.Stderr = tty, tty, tty
|
|
return &xterm{p, pty}, p.Start()
|
|
}
|
|
}
|
|
func PushShell(m *ice.Message, xterm XTerm, cmds []string, cb func(string)) {
|
|
list := [][]string{}
|
|
list = append(list, []string{""})
|
|
lock := task.Lock{}
|
|
m.Go(func() {
|
|
kit.For(cmds, func(cmd string) {
|
|
for {
|
|
m.Sleep300ms()
|
|
if func() bool { defer lock.Lock()(); return len(list[len(list)-1]) > 1 }() {
|
|
break
|
|
}
|
|
}
|
|
m.Debug("cmd %v", cmd)
|
|
fmt.Fprintln(xterm, cmd)
|
|
defer lock.Lock()()
|
|
list = append(list, []string{cmd})
|
|
})
|
|
m.Sleep(m.OptionDefault("interval", "3s"))
|
|
})
|
|
kit.For(xterm, func(res []byte) {
|
|
m.Debug("res %v %v", string(res), res)
|
|
cb(string(res))
|
|
defer lock.Lock()()
|
|
list[len(list)-1] = append(list[len(list)-1], string(res))
|
|
})
|
|
}
|