added some clarifying comments

This commit is contained in:
Rasmus Moorats 2018-08-29 18:15:33 +03:00
parent 8a561cf920
commit ed86579646
9 changed files with 55 additions and 14 deletions

View file

@ -34,6 +34,7 @@ var auth = cmd.Command{
} }
func authRun(param []string) { func authRun(param []string) {
// Authenticating is just another call
setting.Vars.Conn.Call("session", "login", map[string]interface{}{"username": param[0], setting.Vars.Conn.Call("session", "login", map[string]interface{}{"username": param[0],
"password": param[1]}) "password": param[1]})
} }

View file

@ -24,8 +24,10 @@ import (
"strings" "strings"
) )
// CommandList will map all of the command structs to their names
var CommandList = make(map[string]Command) var CommandList = make(map[string]Command)
// Command is the actual struct for commands
type Command struct { type Command struct {
Name string Name string
UsageText string UsageText string
@ -35,14 +37,18 @@ type Command struct {
MaxArg int // Same as above MaxArg int // Same as above
} }
// Register adds command to CommandList. Should be called on init()
func (cmd *Command) Register() { func (cmd *Command) Register() {
CommandList[cmd.Name] = *cmd CommandList[cmd.Name] = *cmd
} }
// Usage prints the command's usage
func (cmd *Command) Usage() { func (cmd *Command) Usage() {
fmt.Println("Usage:", cmd.UsageText) 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) { func (cmd *Command) Execute(line string) {
arg := strings.Split(line, " ") arg := strings.Split(line, " ")
switch { switch {

View file

@ -34,13 +34,15 @@ var dialer = websocket.Dialer{
HandshakeTimeout: 15 * time.Second, HandshakeTimeout: 15 * time.Second,
} }
// Connection houses the current connection's details
type Connection struct { type Connection struct {
Ws *websocket.Conn Ws *websocket.Conn
Key string Key string
User string User string
Id int ID int
} }
// Connect to the specified host
func (c *Connection) Connect(addr string) { func (c *Connection) Connect(addr string) {
u := url.URL{Scheme: "ws", Host: addr, Path: "/"} 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() { func (c *Connection) Disconnect() {
if c.Ws != nil { if c.Ws != nil {
c.Ws.Close() c.Ws.Close()
@ -62,18 +65,20 @@ func (c *Connection) Disconnect() {
} }
} }
// Send the specified interface{} as json
func (c *Connection) Send(request interface{}) { func (c *Connection) Send(request interface{}) {
if c.Ws != nil { if c.Ws != nil {
c.Ws.WriteJSON(request) 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 { if c.Ws != nil {
var r response var r Response
c.Ws.ReadJSON(&r) c.Ws.ReadJSON(&r)
return r return r
} }
return response{} return Response{}
} }

View file

@ -19,16 +19,18 @@
package connection package connection
type response struct { // Response from server is stored in this struct
Id int type Response struct {
ID int
Result []interface{} Result []interface{}
Jsonrpc string 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{} { func (c *Connection) genUbusRequest(method, path, pmethod string, message map[string]interface{}) interface{} {
request := map[string]interface{}{ request := map[string]interface{}{
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": c.Id, "id": c.ID,
"method": method, "method": method,
"params": []interface{}{ "params": []interface{}{
c.Key, c.Key,
@ -41,6 +43,7 @@ func (c *Connection) genUbusRequest(method, path, pmethod string, message map[st
return request return request
} }
// Call a method on the target device
func (c *Connection) Call(path, method string, message map[string]interface{}) { func (c *Connection) Call(path, method string, message map[string]interface{}) {
request := c.genUbusRequest("call", path, method, message) request := c.genUbusRequest("call", path, method, message)
c.Send(request) c.Send(request)
@ -72,9 +75,10 @@ func resultToStr(r int) string {
return "Unknown error" return "Unknown error"
} }
func ParseResponse(r *response) (int, string, map[string]interface{}) { // ParseResponse reads a response and returns data, which is more comfortable to use
var result string func ParseResponse(r *Response) (int, string, map[string]interface{}) {
var data 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) rLen := len(r.Result)
if rLen > 0 { if rLen > 0 {
result = resultToStr(int(r.Result[0].(float64))) result = resultToStr(int(r.Result[0].(float64)))

View file

@ -28,29 +28,38 @@ import (
) )
var ( var (
// Cmd is a makeshift "API" to avoid imports
Cmd = make(chan []string) Cmd = make(chan []string)
In = make(chan interface{}) // In receives incoming messages
In = make(chan interface{})
// Out houses outgoing messages
Out = make(chan interface{}) Out = make(chan interface{})
) )
var ( var (
// Host IP. In reality, this should be read from a file (if it exists)
Host = "192.168.1.1" Host = "192.168.1.1"
) )
// ShellVars house some important structs, so they can be accessed elsewhere
type ShellVars struct { type ShellVars struct {
Conn *connection.Connection Conn *connection.Connection
Completer readline.PrefixCompleter Completer readline.PrefixCompleter
Instance *readline.Instance Instance *readline.Instance
} }
// UpdatePrompt refreshes the prompt and sets it according to current status
func (s *ShellVars) UpdatePrompt() { func (s *ShellVars) UpdatePrompt() {
var prompt string var prompt string
if s.Conn.Ws == nil { if s.Conn.Ws == nil {
// Not connected
prompt = "\033[91miop\033[0;1m$\033[0m " prompt = "\033[91miop\033[0;1m$\033[0m "
} else { } else {
if s.Conn.User == "" { if s.Conn.User == "" {
// Connected but not authenticated
prompt = "\033[32miop\033[0;1m$\033[0m " prompt = "\033[32miop\033[0;1m$\033[0m "
} else { } else {
// Connected and authenticated
prompt = fmt.Sprintf("\033[32miop\033[0m %s\033[0;1m$\033[0m ", s.Conn.User) 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() s.Instance.Refresh()
} }
// UpdateCompleter adds commands registered with .Register() to the autocompleter
func (s *ShellVars) UpdateCompleter(cmdlist map[string]cmd.Command) { func (s *ShellVars) UpdateCompleter(cmdlist map[string]cmd.Command) {
s.Completer = *readline.NewPrefixCompleter() s.Completer = *readline.NewPrefixCompleter()
s.Completer.SetChildren(*new([]readline.PrefixCompleterInterface)) 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 var Vars ShellVars

View file

@ -39,6 +39,7 @@ func filterInput(r rune) (rune, bool) {
return r, true return r, true
} }
// Sv is just Vars for easy access
var Sv = &setting.Vars var Sv = &setting.Vars
func connectionHandler() { func connectionHandler() {
@ -68,14 +69,16 @@ func msgListener() {
response := Sv.Conn.Recv() response := Sv.Conn.Recv()
if response.Jsonrpc != "" { if response.Jsonrpc != "" {
rLen, err, rData := connection.ParseResponse(&response) 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 { if rLen > 1 {
fmt.Println(textmutate.Pprint(rData)) fmt.Println(textmutate.Pprint(rData))
if key, ok := rData["ubus_rpc_session"]; ok { if key, ok := rData["ubus_rpc_session"]; ok {
// See if we have a session key
Sv.Conn.Key = key.(string) Sv.Conn.Key = key.(string)
} }
if data, ok := rData["data"]; ok { if data, ok := rData["data"]; ok {
if user, ok := data.(map[string]interface{})["username"]; 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.Conn.User = user.(string)
} }
} }
@ -88,6 +91,7 @@ func msgListener() {
return return
} }
// Shell provides the CLI interface
func Shell() { func Shell() {
l, err := readline.NewEx(&readline.Config{ l, err := readline.NewEx(&readline.Config{
HistoryFile: "/tmp/iop.tmp", HistoryFile: "/tmp/iop.tmp",

View file

@ -23,6 +23,8 @@ import (
"encoding/json" "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 { func Pprint(input interface{}) string {
out, err := json.MarshalIndent(input, "", " ") out, err := json.MarshalIndent(input, "", " ")
if err == nil { if err == nil {

View file

@ -29,6 +29,8 @@ type parser struct {
Cursor int 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 { func (p *parser) specialChar(offset ...int) bool {
cursor := p.Cursor cursor := p.Cursor
if len(offset) > 0 { if len(offset) > 0 {
@ -42,6 +44,7 @@ func (p *parser) specialChar(offset ...int) bool {
return false return false
} }
// Consume a word until a special char and return it
func (p *parser) word() string { func (p *parser) word() string {
var word strings.Builder var word strings.Builder
@ -56,6 +59,7 @@ func (p *parser) word() string {
return word.String() return word.String()
} }
// Return what the next special char is
func (p *parser) peekChar() rune { func (p *parser) peekChar() rune {
for i, char := range p.Input[p.Cursor:] { for i, char := range p.Input[p.Cursor:] {
if p.specialChar(i) { if p.specialChar(i) {
@ -65,6 +69,10 @@ func (p *parser) peekChar() rune {
return 0 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) { func StrToMap(input string) (map[string]interface{}, int) {
out := make(map[string]interface{}) out := make(map[string]interface{})
p := parser{Input: input, p := parser{Input: input,

View file

@ -20,7 +20,7 @@
package main package main
import ( import (
_ "gitlab.com/neonsea/iopshell/commands" _ "gitlab.com/neonsea/iopshell/commands" // runs .Register() for each command
"gitlab.com/neonsea/iopshell/internal/shell" "gitlab.com/neonsea/iopshell/internal/shell"
) )