5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-12-23 16:55:40 +00:00
yggdrasil-go/src/admin/admin.go

304 lines
7.7 KiB
Go
Raw Normal View History

package admin
2018-01-21 00:17:15 +00:00
2018-06-12 22:50:08 +00:00
import (
"encoding/json"
"errors"
2018-06-12 22:50:08 +00:00
"fmt"
"net"
"net/url"
"os"
2018-06-12 22:50:08 +00:00
"strings"
"time"
"github.com/gologme/log"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
2021-05-23 19:42:26 +00:00
"github.com/yggdrasil-network/yggdrasil-go/src/core"
2018-06-12 22:50:08 +00:00
)
2018-01-21 00:17:15 +00:00
// TODO: Add authentication
2018-01-21 00:17:15 +00:00
type AdminSocket struct {
2021-05-23 19:42:26 +00:00
core *core.Core
log *log.Logger
listenaddr string
listener net.Listener
handlers map[string]handler
done chan struct{}
2018-01-30 00:48:14 +00:00
}
type AdminSocketResponse struct {
Status string `json:"status"`
Request struct {
Name string `json:"request"`
KeepAlive bool `json:"keepalive"`
} `json:"request"`
Response interface{} `json:"response"`
}
2018-05-20 16:21:14 +00:00
type handler struct {
args []string // List of human-readable argument names
handler func(json.RawMessage) (interface{}, error) // First is input map, second is output
}
type ListResponse struct {
2021-05-16 19:53:40 +00:00
List map[string]ListEntry `json:"list"`
}
type ListEntry struct {
Fields []string `json:"fields"`
2018-05-20 16:21:14 +00:00
}
// AddHandler is called for each admin function to add the handler and help documentation to the API.
func (a *AdminSocket) AddHandler(name string, args []string, handlerfunc func(json.RawMessage) (interface{}, error)) error {
if _, ok := a.handlers[strings.ToLower(name)]; ok {
return errors.New("handler already exists")
}
a.handlers[strings.ToLower(name)] = handler{
args: args,
handler: handlerfunc,
}
return nil
2018-01-21 00:17:15 +00:00
}
// Init runs the initial admin setup.
func (a *AdminSocket) Init(c *core.Core, nc *config.NodeConfig, log *log.Logger, options interface{}) error {
2018-01-21 00:17:15 +00:00
a.core = c
a.log = log
a.handlers = make(map[string]handler)
nc.RLock()
a.listenaddr = nc.AdminListen
nc.RUnlock()
a.done = make(chan struct{})
close(a.done) // Start in a done / not-started state
_ = a.AddHandler("list", []string{}, func(_ json.RawMessage) (interface{}, error) {
2021-05-16 19:53:40 +00:00
res := &ListResponse{
List: map[string]ListEntry{},
}
for name, handler := range a.handlers {
2021-05-16 19:53:40 +00:00
res.List[name] = ListEntry{
Fields: handler.args,
}
2019-10-23 10:12:51 +00:00
}
return res, nil
2019-10-23 10:12:51 +00:00
})
return nil
}
func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) {
_ = a.AddHandler("getSelf", []string{}, func(in json.RawMessage) (interface{}, error) {
req := &GetSelfRequest{}
res := &GetSelfResponse{}
if err := json.Unmarshal(in, &req); err != nil {
return nil, err
}
if err := a.getSelfHandler(req, res); err != nil {
return nil, err
}
return res, nil
})
_ = a.AddHandler("getPeers", []string{}, func(in json.RawMessage) (interface{}, error) {
req := &GetPeersRequest{}
res := &GetPeersResponse{}
if err := json.Unmarshal(in, &req); err != nil {
return nil, err
2018-05-20 16:21:14 +00:00
}
if err := a.getPeersHandler(req, res); err != nil {
return nil, err
}
return res, nil
2018-01-30 00:48:14 +00:00
})
_ = a.AddHandler("getDHT", []string{}, func(in json.RawMessage) (interface{}, error) {
req := &GetDHTRequest{}
res := &GetDHTResponse{}
if err := json.Unmarshal(in, &req); err != nil {
return nil, err
2018-02-28 13:43:06 +00:00
}
if err := a.getDHTHandler(req, res); err != nil {
return nil, err
}
return res, nil
2018-02-28 13:43:06 +00:00
})
2021-05-16 21:16:58 +00:00
_ = a.AddHandler("getPaths", []string{}, func(in json.RawMessage) (interface{}, error) {
req := &GetPathsRequest{}
res := &GetPathsResponse{}
if err := json.Unmarshal(in, &req); err != nil {
return nil, err
}
if err := a.getPathsHandler(req, res); err != nil {
return nil, err
}
return res, nil
})
_ = a.AddHandler("getSessions", []string{}, func(in json.RawMessage) (interface{}, error) {
req := &GetSessionsRequest{}
res := &GetSessionsResponse{}
if err := json.Unmarshal(in, &req); err != nil {
return nil, err
}
if err := a.getSessionsHandler(req, res); err != nil {
return nil, err
}
return res, nil
})
2018-05-27 21:13:37 +00:00
}
// Start runs the admin API socket to listen for / respond to admin API calls.
func (a *AdminSocket) Start() error {
if a.listenaddr != "none" && a.listenaddr != "" {
a.done = make(chan struct{})
go a.listen()
}
2018-05-27 21:13:37 +00:00
return nil
2018-01-21 00:17:15 +00:00
}
// IsStarted returns true if the module has been started.
func (a *AdminSocket) IsStarted() bool {
select {
case <-a.done:
// Not blocking, so we're not currently running
return false
default:
// Blocked, so we must have started
return true
}
}
// Stop will stop the admin API and close the socket.
func (a *AdminSocket) Stop() error {
if a.listener != nil {
select {
case <-a.done:
default:
close(a.done)
}
return a.listener.Close()
}
return nil
}
// listen is run by start and manages API connections.
func (a *AdminSocket) listen() {
u, err := url.Parse(a.listenaddr)
if err == nil {
switch strings.ToLower(u.Scheme) {
case "unix":
2018-12-10 00:19:21 +00:00
if _, err := os.Stat(a.listenaddr[7:]); err == nil {
a.log.Debugln("Admin socket", a.listenaddr[7:], "already exists, trying to clean up")
if _, err := net.DialTimeout("unix", a.listenaddr[7:], time.Second*2); err == nil || err.(net.Error).Timeout() {
a.log.Errorln("Admin socket", a.listenaddr[7:], "already exists and is in use by another process")
2019-03-03 14:09:54 +00:00
os.Exit(1)
} else {
if err := os.Remove(a.listenaddr[7:]); err == nil {
a.log.Debugln(a.listenaddr[7:], "was cleaned up")
2019-03-03 14:09:54 +00:00
} else {
a.log.Errorln(a.listenaddr[7:], "already exists and was not cleaned up:", err)
2019-03-03 14:09:54 +00:00
os.Exit(1)
}
}
2018-12-10 00:19:21 +00:00
}
2018-07-07 19:04:11 +00:00
a.listener, err = net.Listen("unix", a.listenaddr[7:])
if err == nil {
switch a.listenaddr[7:8] {
case "@": // maybe abstract namespace
default:
if err := os.Chmod(a.listenaddr[7:], 0660); err != nil {
a.log.Warnln("WARNING:", a.listenaddr[:7], "may have unsafe permissions!")
}
2018-12-10 00:19:21 +00:00
}
}
case "tcp":
2018-07-07 19:04:11 +00:00
a.listener, err = net.Listen("tcp", u.Host)
default:
2018-07-08 09:37:20 +00:00
// err = errors.New(fmt.Sprint("protocol not supported: ", u.Scheme))
a.listener, err = net.Listen("tcp", a.listenaddr)
}
} else {
2018-07-07 19:04:11 +00:00
a.listener, err = net.Listen("tcp", a.listenaddr)
}
2018-01-21 00:17:15 +00:00
if err != nil {
a.log.Errorf("Admin socket failed to listen: %v", err)
2018-01-21 00:17:15 +00:00
os.Exit(1)
}
a.log.Infof("%s admin socket listening on %s",
2018-07-07 19:04:11 +00:00
strings.ToUpper(a.listener.Addr().Network()),
a.listener.Addr().String())
defer a.listener.Close()
2018-01-21 00:17:15 +00:00
for {
2018-07-07 19:04:11 +00:00
conn, err := a.listener.Accept()
2018-01-21 00:17:15 +00:00
if err == nil {
2018-12-17 19:06:52 +00:00
go a.handleRequest(conn)
} else {
select {
case <-a.done:
// Not blocked, so we havent started or already stopped
return
default:
// Blocked, so we're supposed to keep running
}
2018-01-21 00:17:15 +00:00
}
}
}
// handleRequest calls the request handler for each request sent to the admin API.
func (a *AdminSocket) handleRequest(conn net.Conn) {
2018-05-20 16:21:14 +00:00
decoder := json.NewDecoder(conn)
decoder.DisallowUnknownFields()
2018-05-20 16:21:14 +00:00
encoder := json.NewEncoder(conn)
encoder.SetIndent("", " ")
defer conn.Close()
2018-05-20 16:21:14 +00:00
defer func() {
r := recover()
if r != nil {
a.log.Debugln("Admin socket error:", r)
if err := encoder.Encode(&ErrorResponse{
Error: "Check your syntax and input types",
}); err != nil {
a.log.Debugln("Admin socket JSON encode error:", err)
}
conn.Close()
}
}()
2018-05-20 16:21:14 +00:00
for {
var err error
var buf json.RawMessage
_ = decoder.Decode(&buf)
var resp AdminSocketResponse
resp.Status = "success"
if err = json.Unmarshal(buf, &resp.Request); err == nil {
if resp.Request.Name == "" {
resp.Status = "error"
resp.Response = &ErrorResponse{
Error: "No request specified",
}
} else if h, ok := a.handlers[strings.ToLower(resp.Request.Name)]; ok {
resp.Response, err = h.handler(buf)
if err != nil {
resp.Status = "error"
resp.Response = &ErrorResponse{
Error: err.Error(),
2018-05-20 16:21:14 +00:00
}
}
} else {
resp.Status = "error"
resp.Response = &ErrorResponse{
Error: fmt.Sprintf("Unknown action '%s', try 'list' for help", resp.Request.Name),
}
2018-05-20 16:21:14 +00:00
}
}
if err = encoder.Encode(resp); err != nil {
a.log.Debugln("Encode error:", err)
2018-05-20 16:21:14 +00:00
}
if !resp.Request.KeepAlive {
break
} else {
continue
}
2018-05-20 16:21:14 +00:00
}
2018-01-30 00:48:14 +00:00
}