package main import ( "fmt" "github.com/chzyer/readline" "reflect" "strings" "sync" "time" ) type serverStr struct { userInteractionMutex sync.Mutex // all user interactions (controlserver or web access) must be serialized: } func isClosed(hdl interface{}) bool { v := reflect.ValueOf(hdl) if v.Kind() != reflect.Ptr { return false } v = v.Elem() if v.Kind() != reflect.Struct { return false } v = v.FieldByName("target").Elem() if v.Kind() != reflect.Ptr { return false } v = v.Elem() if v.Kind() != reflect.Struct { return false } v = v.FieldByName("closed") if v.Kind() != reflect.Int32 { return false } closed := v.Int() return closed == 1 } func (srv *serverStr) StartCLIServer() { go func() { port := 1234 prompt := "tst " cfg := &readline.Config{ Prompt: prompt + " > ", HistoryFile: "readlineHist", } handleFunc := func(rl *readline.Instance) { for { if func() bool { // so we can "defer srv.userInteractionMutex.Unlock()" on the level of each line. "true" means to break the for loop, "false" means to continue. line, err := rl.Readline() print(line) if err != nil { return true } //fmt.Fprintln(rl.Stdout(), "receive:"+line) line = strings.TrimSpace(line) srv.userInteractionMutex.Lock() defer srv.userInteractionMutex.Unlock() if line == "pause" { time.Sleep(time.Second * 10) print("paused") } if line == "close" { return true } stdout := rl.Stdout() if isClosed(stdout) { return true } n, err := fmt.Fprintln(stdout, "done: "+line) print(n) return false }() { break } } } err := readline.ListenRemote("tcp", fmt.Sprintf(":%d", port), cfg, handleFunc) if err != nil { println(err.Error()) } }() } func main() { var s serverStr s.StartCLIServer() for { time.Sleep(time.Second) } }