/* Copyright (c) 2018 Rasmus Moorats (neonsea) This file is part of iopshell. iopshell is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. iopshell is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with iopshell. If not, see <https://www.gnu.org/licenses/>. */ package shell import ( "fmt" "io" "strings" "github.com/chzyer/readline" "github.com/neonsea/iopshell/internal/cmd" "github.com/neonsea/iopshell/internal/connection" "github.com/neonsea/iopshell/internal/setting" "github.com/neonsea/iopshell/internal/textmutate" ) func filterInput(r rune) (rune, bool) { switch r { case readline.CharCtrlZ: return r, false } return r, true } // Sv is just Vars for easy access var Sv = &setting.Vars func connListener() { for { cmd := <-setting.ConnChannel switch cmd[0] { case "connect": Sv.Conn.Connect(cmd[1]) go msgListener() Sv.UpdatePrompt() case "disconnect": Sv.Conn.Disconnect() Sv.UpdatePrompt() } } } func scriptListener() { for { cmd := <-setting.ScriptIn switch cmd[0] { case "run": setting.ScriptRet <- runScript(cmd[1]) } } } func msgParser() { for { output := <-setting.Out Sv.Conn.Send(output) } } func passResponse(res connection.Response) { setting.PassBack <- res setting.PassBackID = -1 } func msgListener() { for Sv.Conn.Ws != nil { response := Sv.Conn.Recv() if response.Jsonrpc != "" { rLen, err, rData := connection.ParseResponse(&response) if response.ID == setting.PassBackID { passResponse(response) } fmt.Printf("\n%d: %s\n", response.ID, err) if rLen > 1 { fmt.Println(textmutate.Pprint(rData)) if key, ok := rData["ubus_rpc_session"]; ok { // See if we have a session key Sv.Conn.Key = key.(string) if data, ok := rData["data"]; ok { if user, ok := data.(map[string]interface{})["username"]; ok { // If we just logged in, we get our username Sv.Conn.User = user.(string) } } } } Sv.UpdatePrompt() } } return } // Shell provides the CLI interface func Shell(script string) error { Sv.Instance = prepareShell() defer Sv.Instance.Close() Sv.UpdatePrompt() rc := GetRCFile() if rc != "" { runScript(rc) } if script != "" { // run script and exit return runScript(script) } for { line, err := Sv.Instance.Readline() if err == io.EOF { break } else if err == readline.ErrInterrupt { continue } parseLine(line) } return nil } func parseLine(line string) { line = strings.TrimSpace(line) command := strings.Split(line, " ")[0] if val, k := cmd.CommandList[command]; k { val.Execute(line) } else if command == "" { return } else { fmt.Printf("Unknown command '%s'\n", line) } return } // PrepareShell sets up some goroutines and the connection handler, returns the Readline instance func prepareShell() *readline.Instance { l, err := readline.NewEx(&readline.Config{ HistoryFile: "/tmp/iop.tmp", AutoComplete: &Sv.Completer, InterruptPrompt: "^C", EOFPrompt: "^D", HistorySearchFold: true, FuncFilterInputRune: filterInput, }) if err != nil { panic(err) } Sv.Conn = new(connection.Connection) go connListener() go scriptListener() go msgParser() Sv.UpdateCompleter(cmd.CommandList) return l }