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) {
|
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]})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue