added some clarifying comments
This commit is contained in:
parent
8a561cf920
commit
ed86579646
9 changed files with 55 additions and 14 deletions
|
@ -34,6 +34,7 @@ var auth = cmd.Command{
|
|||
}
|
||||
|
||||
func authRun(param []string) {
|
||||
// Authenticating is just another call
|
||||
setting.Vars.Conn.Call("session", "login", map[string]interface{}{"username": param[0],
|
||||
"password": param[1]})
|
||||
}
|
||||
|
|
|
@ -24,8 +24,10 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// CommandList will map all of the command structs to their names
|
||||
var CommandList = make(map[string]Command)
|
||||
|
||||
// Command is the actual struct for commands
|
||||
type Command struct {
|
||||
Name string
|
||||
UsageText string
|
||||
|
@ -35,14 +37,18 @@ type Command struct {
|
|||
MaxArg int // Same as above
|
||||
}
|
||||
|
||||
// Register adds command to CommandList. Should be called on init()
|
||||
func (cmd *Command) Register() {
|
||||
CommandList[cmd.Name] = *cmd
|
||||
}
|
||||
|
||||
// Usage prints the command's usage
|
||||
func (cmd *Command) Usage() {
|
||||
fmt.Println("Usage:", cmd.UsageText)
|
||||
}
|
||||
|
||||
// Execute runs the command's specified "Action" function,
|
||||
// if MinArg and MaxArg are within acceptable bounds
|
||||
func (cmd *Command) Execute(line string) {
|
||||
arg := strings.Split(line, " ")
|
||||
switch {
|
||||
|
|
|
@ -34,13 +34,15 @@ var dialer = websocket.Dialer{
|
|||
HandshakeTimeout: 15 * time.Second,
|
||||
}
|
||||
|
||||
// Connection houses the current connection's details
|
||||
type Connection struct {
|
||||
Ws *websocket.Conn
|
||||
Key string
|
||||
User string
|
||||
Id int
|
||||
ID int
|
||||
}
|
||||
|
||||
// Connect to the specified host
|
||||
func (c *Connection) Connect(addr string) {
|
||||
u := url.URL{Scheme: "ws", Host: addr, Path: "/"}
|
||||
|
||||
|
@ -55,6 +57,7 @@ func (c *Connection) Connect(addr string) {
|
|||
}
|
||||
}
|
||||
|
||||
// Disconnect from the host
|
||||
func (c *Connection) Disconnect() {
|
||||
if c.Ws != nil {
|
||||
c.Ws.Close()
|
||||
|
@ -62,18 +65,20 @@ func (c *Connection) Disconnect() {
|
|||
}
|
||||
}
|
||||
|
||||
// Send the specified interface{} as json
|
||||
func (c *Connection) Send(request interface{}) {
|
||||
if c.Ws != nil {
|
||||
c.Ws.WriteJSON(request)
|
||||
c.Id++
|
||||
c.ID++
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Connection) Recv() response {
|
||||
// Recv reads a json response into a response struct
|
||||
func (c *Connection) Recv() Response {
|
||||
if c.Ws != nil {
|
||||
var r response
|
||||
var r Response
|
||||
c.Ws.ReadJSON(&r)
|
||||
return r
|
||||
}
|
||||
return response{}
|
||||
return Response{}
|
||||
}
|
||||
|
|
|
@ -19,16 +19,18 @@
|
|||
|
||||
package connection
|
||||
|
||||
type response struct {
|
||||
Id int
|
||||
// Response from server is stored in this struct
|
||||
type Response struct {
|
||||
ID int
|
||||
Result []interface{}
|
||||
Jsonrpc string
|
||||
}
|
||||
|
||||
// Generate a request interface{} which can be sent to ubus
|
||||
func (c *Connection) genUbusRequest(method, path, pmethod string, message map[string]interface{}) interface{} {
|
||||
request := map[string]interface{}{
|
||||
"jsonrpc": "2.0",
|
||||
"id": c.Id,
|
||||
"id": c.ID,
|
||||
"method": method,
|
||||
"params": []interface{}{
|
||||
c.Key,
|
||||
|
@ -41,6 +43,7 @@ func (c *Connection) genUbusRequest(method, path, pmethod string, message map[st
|
|||
return request
|
||||
}
|
||||
|
||||
// Call a method on the target device
|
||||
func (c *Connection) Call(path, method string, message map[string]interface{}) {
|
||||
request := c.genUbusRequest("call", path, method, message)
|
||||
c.Send(request)
|
||||
|
@ -72,9 +75,10 @@ func resultToStr(r int) string {
|
|||
return "Unknown error"
|
||||
}
|
||||
|
||||
func ParseResponse(r *response) (int, string, map[string]interface{}) {
|
||||
var result string
|
||||
var data map[string]interface{}
|
||||
// ParseResponse reads a response and returns data, which is more comfortable to use
|
||||
func ParseResponse(r *Response) (int, string, map[string]interface{}) {
|
||||
var result string // the "error code" should be human-readable
|
||||
var data map[string]interface{} // if the call returned data, map it
|
||||
rLen := len(r.Result)
|
||||
if rLen > 0 {
|
||||
result = resultToStr(int(r.Result[0].(float64)))
|
||||
|
|
|
@ -28,29 +28,38 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
// Cmd is a makeshift "API" to avoid imports
|
||||
Cmd = make(chan []string)
|
||||
In = make(chan interface{})
|
||||
// In receives incoming messages
|
||||
In = make(chan interface{})
|
||||
// Out houses outgoing messages
|
||||
Out = make(chan interface{})
|
||||
)
|
||||
|
||||
var (
|
||||
// Host IP. In reality, this should be read from a file (if it exists)
|
||||
Host = "192.168.1.1"
|
||||
)
|
||||
|
||||
// ShellVars house some important structs, so they can be accessed elsewhere
|
||||
type ShellVars struct {
|
||||
Conn *connection.Connection
|
||||
Completer readline.PrefixCompleter
|
||||
Instance *readline.Instance
|
||||
}
|
||||
|
||||
// UpdatePrompt refreshes the prompt and sets it according to current status
|
||||
func (s *ShellVars) UpdatePrompt() {
|
||||
var prompt string
|
||||
if s.Conn.Ws == nil {
|
||||
// Not connected
|
||||
prompt = "\033[91miop\033[0;1m$\033[0m "
|
||||
} else {
|
||||
if s.Conn.User == "" {
|
||||
// Connected but not authenticated
|
||||
prompt = "\033[32miop\033[0;1m$\033[0m "
|
||||
} else {
|
||||
// Connected and authenticated
|
||||
prompt = fmt.Sprintf("\033[32miop\033[0m %s\033[0;1m$\033[0m ", s.Conn.User)
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +67,7 @@ func (s *ShellVars) UpdatePrompt() {
|
|||
s.Instance.Refresh()
|
||||
}
|
||||
|
||||
// UpdateCompleter adds commands registered with .Register() to the autocompleter
|
||||
func (s *ShellVars) UpdateCompleter(cmdlist map[string]cmd.Command) {
|
||||
s.Completer = *readline.NewPrefixCompleter()
|
||||
s.Completer.SetChildren(*new([]readline.PrefixCompleterInterface))
|
||||
|
@ -73,4 +83,5 @@ func (s *ShellVars) UpdateCompleter(cmdlist map[string]cmd.Command) {
|
|||
}
|
||||
}
|
||||
|
||||
// Vars is an instance of ShellVars
|
||||
var Vars ShellVars
|
||||
|
|
|
@ -39,6 +39,7 @@ func filterInput(r rune) (rune, bool) {
|
|||
return r, true
|
||||
}
|
||||
|
||||
// Sv is just Vars for easy access
|
||||
var Sv = &setting.Vars
|
||||
|
||||
func connectionHandler() {
|
||||
|
@ -68,14 +69,16 @@ func msgListener() {
|
|||
response := Sv.Conn.Recv()
|
||||
if response.Jsonrpc != "" {
|
||||
rLen, err, rData := connection.ParseResponse(&response)
|
||||
fmt.Printf("\n%d: %s\n", response.Id, err)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +91,7 @@ func msgListener() {
|
|||
return
|
||||
}
|
||||
|
||||
// Shell provides the CLI interface
|
||||
func Shell() {
|
||||
l, err := readline.NewEx(&readline.Config{
|
||||
HistoryFile: "/tmp/iop.tmp",
|
||||
|
|
|
@ -23,6 +23,8 @@ import (
|
|||
"encoding/json"
|
||||
)
|
||||
|
||||
// Pprint pretty-prints a json input (interface{})
|
||||
// In the future, this should be replaced with a custom function
|
||||
func Pprint(input interface{}) string {
|
||||
out, err := json.MarshalIndent(input, "", " ")
|
||||
if err == nil {
|
||||
|
|
|
@ -29,6 +29,8 @@ type parser struct {
|
|||
Cursor int
|
||||
}
|
||||
|
||||
// Simply returns whether the current char (or char + offset) is
|
||||
// a special char (one specified in SChars)
|
||||
func (p *parser) specialChar(offset ...int) bool {
|
||||
cursor := p.Cursor
|
||||
if len(offset) > 0 {
|
||||
|
@ -42,6 +44,7 @@ func (p *parser) specialChar(offset ...int) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Consume a word until a special char and return it
|
||||
func (p *parser) word() string {
|
||||
var word strings.Builder
|
||||
|
||||
|
@ -56,6 +59,7 @@ func (p *parser) word() string {
|
|||
return word.String()
|
||||
}
|
||||
|
||||
// Return what the next special char is
|
||||
func (p *parser) peekChar() rune {
|
||||
for i, char := range p.Input[p.Cursor:] {
|
||||
if p.specialChar(i) {
|
||||
|
@ -65,6 +69,10 @@ func (p *parser) peekChar() rune {
|
|||
return 0
|
||||
}
|
||||
|
||||
// StrToMap takes an input string and creates a map out of it, for use
|
||||
// in json requests. For example,
|
||||
// "key1:key1-1:value1-1,key1-2:value1-2;key2:value2" returns
|
||||
// {"key1": {"key1-1": "value1-1", "key1-2": "value1-2"}, "key2": "value2"}
|
||||
func StrToMap(input string) (map[string]interface{}, int) {
|
||||
out := make(map[string]interface{})
|
||||
p := parser{Input: input,
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
_ "gitlab.com/neonsea/iopshell/commands"
|
||||
_ "gitlab.com/neonsea/iopshell/commands" // runs .Register() for each command
|
||||
"gitlab.com/neonsea/iopshell/internal/shell"
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in a new issue