diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 0509a30..b6bc7f3 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -44,17 +44,20 @@ func (a *admin) init(c *Core, listenaddr string) { a.core = c a.listenaddr = listenaddr a.addHandler("help", nil, func(in admin_info) (admin_info, error) { - handlers := make(map[string][]string) + handlers := make(map[string]interface{}) for _, handler := range a.handlers { - handlers[handler.name] = handler.args + handlers[handler.name] = admin_info{"fields": handler.args} } - return admin_info{"handlers": handlers}, nil + return admin_info{"help": handlers}, nil }) a.addHandler("dot", []string{}, func(in admin_info) (admin_info, error) { return admin_info{"dot": string(a.getResponse_dot())}, nil }) a.addHandler("getSelf", []string{}, func(in admin_info) (admin_info, error) { - return admin_info{"self": a.getData_getSelf().asMap()}, nil + self := a.getData_getSelf().asMap() + ip := fmt.Sprint(self["ip"]) + delete(self, "ip") + return admin_info{"self": admin_info{ip: self}}, nil }) a.addHandler("getPeers", []string{}, func(in admin_info) (admin_info, error) { sort := "ip" @@ -133,14 +136,15 @@ func (a *admin) init(c *Core, listenaddr string) { a.addHandler("getTunTap", []string{}, func(in admin_info) (r admin_info, e error) { defer func() { recover() - r = admin_info{"name": "none"} + r = admin_info{"none": admin_info{}} e = nil }() return admin_info{ - "name": a.core.tun.iface.Name(), - "tap_mode": a.core.tun.iface.IsTAP(), - "mtu": a.core.tun.mtu, + a.core.tun.iface.Name(): admin_info{ + "tap_mode": a.core.tun.iface.IsTAP(), + "mtu": a.core.tun.mtu, + }, }, nil }) a.addHandler("setTunTap", []string{"name", "[tap_mode]", "[mtu]"}, func(in admin_info) (admin_info, error) { @@ -162,9 +166,10 @@ func (a *admin) init(c *Core, listenaddr string) { return admin_info{}, errors.New("Failed to configure adapter") } else { return admin_info{ - "name": a.core.tun.iface.Name(), - "tap_mode": a.core.tun.iface.IsTAP(), - "mtu": ifmtu, + a.core.tun.iface.Name(): admin_info{ + "tap_mode": a.core.tun.iface.IsTAP(), + "mtu": ifmtu, + }, }, nil } }) @@ -273,7 +278,7 @@ func (a *admin) handleRequest(conn net.Conn) { if _, ok := recv[arg]; !ok { send = admin_info{ "status": "error", - "error": "Expected field missing", + "error": "Expected field missing: " + arg, "expecting": arg, } break handlers @@ -432,7 +437,7 @@ func (a *admin) getData_getPeers() []admin_nodeInfo { info := admin_nodeInfo{ {"ip", net.IP(addr[:]).String()}, {"port", port}, - {"uptime", fmt.Sprint(time.Since(p.firstSeen))}, + {"uptime", int(time.Since(p.firstSeen).Seconds())}, {"bytes_sent", atomic.LoadUint64(&p.bytesSent)}, {"bytes_recvd", atomic.LoadUint64(&p.bytesRecvd)}, } @@ -476,7 +481,7 @@ func (a *admin) getData_getDHT() []admin_nodeInfo { {"coords", fmt.Sprint(v.coords)}, {"bucket", i}, {"peer_only", isPeer}, - {"last_seen", fmt.Sprint(now.Sub(v.recv))}, + {"last_seen", int(now.Sub(v.recv).Seconds())}, } infos = append(infos, info) } diff --git a/yggdrasilctl.go b/yggdrasilctl.go index ad05963..2fbebf7 100644 --- a/yggdrasilctl.go +++ b/yggdrasilctl.go @@ -4,6 +4,7 @@ import "flag" import "fmt" import "strings" import "net" +import "sort" import "encoding/json" import "strconv" import "os" @@ -12,11 +13,12 @@ type admin_info map[string]interface{} func main() { server := flag.String("endpoint", "localhost:9001", "Admin socket endpoint") + injson := flag.Bool("json", false, "Output in JSON format") flag.Parse() args := flag.Args() if len(args) == 0 { - fmt.Println("usage:", os.Args[0], "[-endpoint=localhost:9001] command [key=value] [...]") + fmt.Println("usage:", os.Args[0], "[-endpoint=localhost:9001] [-json] command [key=value] [...]") fmt.Println("example:", os.Args[0], "getPeers") fmt.Println("example:", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false") fmt.Println("example:", os.Args[0], "-endpoint=localhost:9001 getDHT") @@ -58,19 +60,133 @@ func main() { panic(err) } if err := decoder.Decode(&recv); err == nil { + if recv["status"] == "error" { + if err, ok := recv["error"]; ok { + fmt.Println("Error:", err) + } else { + fmt.Println("Unspecified error occured") + } + os.Exit(1) + } if _, ok := recv["request"]; !ok { - fmt.Println("Missing request") + fmt.Println("Missing request in response (malformed response?)") return } if _, ok := recv["response"]; !ok { - fmt.Println("Missing response") + fmt.Println("Missing response body (malformed response?)") return } req := recv["request"].(map[string]interface{}) res := recv["response"].(map[string]interface{}) + + if *injson { + if json, err := json.MarshalIndent(res, "", " "); err == nil { + fmt.Println(string(json)) + } + os.Exit(0) + } + switch req["request"] { case "dot": fmt.Println(res["dot"]) + case "help", "getPeers", "getSwitchPeers", "getDHT", "getSessions": + maxWidths := make(map[string]int) + var keyOrder []string + keysOrdered := false + + for _, tlv := range res { + for slk, slv := range tlv.(map[string]interface{}) { + if !keysOrdered { + for k := range slv.(map[string]interface{}) { + keyOrder = append(keyOrder, fmt.Sprint(k)) + } + sort.Strings(keyOrder) + keysOrdered = true + } + for k, v := range slv.(map[string]interface{}) { + if len(fmt.Sprint(slk)) > maxWidths["key"] { + maxWidths["key"] = len(fmt.Sprint(slk)) + } + if len(fmt.Sprint(v)) > maxWidths[k] { + maxWidths[k] = len(fmt.Sprint(v)) + if maxWidths[k] < len(k) { + maxWidths[k] = len(k) + } + } + } + } + + if len(keyOrder) > 0 { + fmt.Printf("%-"+fmt.Sprint(maxWidths["key"])+"s ", "") + for _, v := range keyOrder { + fmt.Printf("%-"+fmt.Sprint(maxWidths[v])+"s ", v) + } + fmt.Println() + } + + for slk, slv := range tlv.(map[string]interface{}) { + fmt.Printf("%-"+fmt.Sprint(maxWidths["key"])+"s ", slk) + for _, k := range keyOrder { + preformatted := slv.(map[string]interface{})[k] + var formatted string + switch k { + case "bytes_sent", "bytes_recvd": + formatted = fmt.Sprintf("%d", uint(preformatted.(float64))) + case "uptime", "last_seen": + seconds := uint(preformatted.(float64)) % 60 + minutes := uint(preformatted.(float64)/60) % 60 + hours := uint(preformatted.(float64) / 60 / 60) + formatted = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds) + default: + formatted = fmt.Sprint(preformatted) + } + fmt.Printf("%-"+fmt.Sprint(maxWidths[k])+"s ", formatted) + } + fmt.Println() + } + } + case "getTunTap", "setTunTap": + for k, v := range res { + fmt.Println("Interface name:", k) + if mtu, ok := v.(map[string]interface{})["mtu"].(float64); ok { + fmt.Println("Interface MTU:", mtu) + } + if tap_mode, ok := v.(map[string]interface{})["tap_mode"].(bool); ok { + fmt.Println("TAP mode:", tap_mode) + } + } + case "addPeer", "removePeer", "addAllowedBoxPub", "removeAllowedBoxPub": + if _, ok := res["added"]; ok { + for _, v := range res["added"].([]interface{}) { + fmt.Println("Added:", fmt.Sprint(v)) + } + } + if _, ok := res["not_added"]; ok { + for _, v := range res["not_added"].([]interface{}) { + fmt.Println("Not added:", fmt.Sprint(v)) + } + } + if _, ok := res["removed"]; ok { + for _, v := range res["removed"].([]interface{}) { + fmt.Println("Removed:", fmt.Sprint(v)) + } + } + if _, ok := res["not_removed"]; ok { + for _, v := range res["not_removed"].([]interface{}) { + fmt.Println("Not removed:", fmt.Sprint(v)) + } + } + case "getAllowedBoxPubs": + if _, ok := res["allowed_box_pubs"]; !ok { + fmt.Println("All connections are allowed") + } else if res["allowed_box_pubs"] == nil { + fmt.Println("All connections are allowed") + } else { + fmt.Println("Connections are allowed only from the following public box keys:") + for _, v := range res["allowed_box_pubs"].([]interface{}) { + fmt.Println("-", v) + } + } default: if json, err := json.MarshalIndent(recv["response"], "", " "); err == nil { fmt.Println(string(json))