github.com/yggdrasil-network/yggdrasil-go@v0.5.6/src/admin/admin.go (about)

     1  package admin
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"net/url"
     9  	"os"
    10  	"sort"
    11  
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/yggdrasil-network/yggdrasil-go/src/core"
    16  )
    17  
    18  // TODO: Add authentication
    19  
    20  type AdminSocket struct {
    21  	core     *core.Core
    22  	log      core.Logger
    23  	listener net.Listener
    24  	handlers map[string]handler
    25  	done     chan struct{}
    26  	config   struct {
    27  		listenaddr ListenAddress
    28  	}
    29  }
    30  
    31  type AdminSocketRequest struct {
    32  	Name      string          `json:"request"`
    33  	Arguments json.RawMessage `json:"arguments,omitempty"`
    34  	KeepAlive bool            `json:"keepalive,omitempty"`
    35  }
    36  
    37  type AdminSocketResponse struct {
    38  	Status   string             `json:"status"`
    39  	Error    string             `json:"error,omitempty"`
    40  	Request  AdminSocketRequest `json:"request"`
    41  	Response json.RawMessage    `json:"response"`
    42  }
    43  
    44  type handler struct {
    45  	desc    string              // What does the endpoint do?
    46  	args    []string            // List of human-readable argument names
    47  	handler core.AddHandlerFunc // First is input map, second is output
    48  }
    49  
    50  type ListResponse struct {
    51  	List []ListEntry `json:"list"`
    52  }
    53  
    54  type ListEntry struct {
    55  	Command     string   `json:"command"`
    56  	Description string   `json:"description"`
    57  	Fields      []string `json:"fields,omitempty"`
    58  }
    59  
    60  // AddHandler is called for each admin function to add the handler and help documentation to the API.
    61  func (a *AdminSocket) AddHandler(name, desc string, args []string, handlerfunc core.AddHandlerFunc) error {
    62  	if _, ok := a.handlers[strings.ToLower(name)]; ok {
    63  		return errors.New("handler already exists")
    64  	}
    65  	a.handlers[strings.ToLower(name)] = handler{
    66  		desc:    desc,
    67  		args:    args,
    68  		handler: handlerfunc,
    69  	}
    70  	return nil
    71  }
    72  
    73  // Init runs the initial admin setup.
    74  func New(c *core.Core, log core.Logger, opts ...SetupOption) (*AdminSocket, error) {
    75  	a := &AdminSocket{
    76  		core:     c,
    77  		log:      log,
    78  		handlers: make(map[string]handler),
    79  	}
    80  	for _, opt := range opts {
    81  		a._applyOption(opt)
    82  	}
    83  	if a.config.listenaddr == "none" || a.config.listenaddr == "" {
    84  		return nil, nil
    85  	}
    86  	_ = a.AddHandler("list", "List available commands", []string{}, func(_ json.RawMessage) (interface{}, error) {
    87  		res := &ListResponse{}
    88  		for name, handler := range a.handlers {
    89  			res.List = append(res.List, ListEntry{
    90  				Command:     name,
    91  				Description: handler.desc,
    92  				Fields:      handler.args,
    93  			})
    94  		}
    95  		sort.SliceStable(res.List, func(i, j int) bool {
    96  			return strings.Compare(res.List[i].Command, res.List[j].Command) < 0
    97  		})
    98  		return res, nil
    99  	})
   100  	a.done = make(chan struct{})
   101  	go a.listen()
   102  	return a, a.core.SetAdmin(a)
   103  }
   104  
   105  func (a *AdminSocket) SetupAdminHandlers() {
   106  	_ = a.AddHandler(
   107  		"getSelf", "Show details about this node", []string{},
   108  		func(in json.RawMessage) (interface{}, error) {
   109  			req := &GetSelfRequest{}
   110  			res := &GetSelfResponse{}
   111  			if err := json.Unmarshal(in, &req); err != nil {
   112  				return nil, err
   113  			}
   114  			if err := a.getSelfHandler(req, res); err != nil {
   115  				return nil, err
   116  			}
   117  			return res, nil
   118  		},
   119  	)
   120  	_ = a.AddHandler(
   121  		"getPeers", "Show directly connected peers", []string{},
   122  		func(in json.RawMessage) (interface{}, error) {
   123  			req := &GetPeersRequest{}
   124  			res := &GetPeersResponse{}
   125  			if err := json.Unmarshal(in, &req); err != nil {
   126  				return nil, err
   127  			}
   128  			if err := a.getPeersHandler(req, res); err != nil {
   129  				return nil, err
   130  			}
   131  			return res, nil
   132  		},
   133  	)
   134  	_ = a.AddHandler(
   135  		"getTree", "Show known Tree entries", []string{},
   136  		func(in json.RawMessage) (interface{}, error) {
   137  			req := &GetTreeRequest{}
   138  			res := &GetTreeResponse{}
   139  			if err := json.Unmarshal(in, &req); err != nil {
   140  				return nil, err
   141  			}
   142  			if err := a.getTreeHandler(req, res); err != nil {
   143  				return nil, err
   144  			}
   145  			return res, nil
   146  		},
   147  	)
   148  	_ = a.AddHandler(
   149  		"getPaths", "Show established paths through this node", []string{},
   150  		func(in json.RawMessage) (interface{}, error) {
   151  			req := &GetPathsRequest{}
   152  			res := &GetPathsResponse{}
   153  			if err := json.Unmarshal(in, &req); err != nil {
   154  				return nil, err
   155  			}
   156  			if err := a.getPathsHandler(req, res); err != nil {
   157  				return nil, err
   158  			}
   159  			return res, nil
   160  		},
   161  	)
   162  	_ = a.AddHandler(
   163  		"getSessions", "Show established traffic sessions with remote nodes", []string{},
   164  		func(in json.RawMessage) (interface{}, error) {
   165  			req := &GetSessionsRequest{}
   166  			res := &GetSessionsResponse{}
   167  			if err := json.Unmarshal(in, &req); err != nil {
   168  				return nil, err
   169  			}
   170  			if err := a.getSessionsHandler(req, res); err != nil {
   171  				return nil, err
   172  			}
   173  			return res, nil
   174  		},
   175  	)
   176  	_ = a.AddHandler(
   177  		"addPeer", "Add a peer to the peer list", []string{"uri", "interface"},
   178  		func(in json.RawMessage) (interface{}, error) {
   179  			req := &AddPeerRequest{}
   180  			res := &AddPeerResponse{}
   181  			if err := json.Unmarshal(in, &req); err != nil {
   182  				return nil, err
   183  			}
   184  			if err := a.addPeerHandler(req, res); err != nil {
   185  				return nil, err
   186  			}
   187  			return res, nil
   188  		},
   189  	)
   190  	_ = a.AddHandler(
   191  		"removePeer", "Remove a peer from the peer list", []string{"uri", "interface"},
   192  		func(in json.RawMessage) (interface{}, error) {
   193  			req := &RemovePeerRequest{}
   194  			res := &RemovePeerResponse{}
   195  			if err := json.Unmarshal(in, &req); err != nil {
   196  				return nil, err
   197  			}
   198  			if err := a.removePeerHandler(req, res); err != nil {
   199  				return nil, err
   200  			}
   201  			return res, nil
   202  		},
   203  	)
   204  	//_ = a.AddHandler("getNodeInfo", []string{"key"}, t.proto.nodeinfo.nodeInfoAdminHandler)
   205  	//_ = a.AddHandler("debug_remoteGetSelf", []string{"key"}, t.proto.getSelfHandler)
   206  	//_ = a.AddHandler("debug_remoteGetPeers", []string{"key"}, t.proto.getPeersHandler)
   207  	//_ = a.AddHandler("debug_remoteGetDHT", []string{"key"}, t.proto.getDHTHandler)
   208  }
   209  
   210  // IsStarted returns true if the module has been started.
   211  func (a *AdminSocket) IsStarted() bool {
   212  	select {
   213  	case <-a.done:
   214  		// Not blocking, so we're not currently running
   215  		return false
   216  	default:
   217  		// Blocked, so we must have started
   218  		return true
   219  	}
   220  }
   221  
   222  // Stop will stop the admin API and close the socket.
   223  func (a *AdminSocket) Stop() error {
   224  	if a == nil {
   225  		return nil
   226  	}
   227  	if a.listener != nil {
   228  		select {
   229  		case <-a.done:
   230  		default:
   231  			close(a.done)
   232  		}
   233  		return a.listener.Close()
   234  	}
   235  	return nil
   236  }
   237  
   238  // listen is run by start and manages API connections.
   239  func (a *AdminSocket) listen() {
   240  	listenaddr := string(a.config.listenaddr)
   241  	u, err := url.Parse(listenaddr)
   242  	if err == nil {
   243  		switch strings.ToLower(u.Scheme) {
   244  		case "unix":
   245  			if _, err := os.Stat(listenaddr[7:]); err == nil {
   246  				a.log.Debugln("Admin socket", listenaddr[7:], "already exists, trying to clean up")
   247  				if _, err := net.DialTimeout("unix", listenaddr[7:], time.Second*2); err == nil || err.(net.Error).Timeout() {
   248  					a.log.Errorln("Admin socket", listenaddr[7:], "already exists and is in use by another process")
   249  					os.Exit(1)
   250  				} else {
   251  					if err := os.Remove(listenaddr[7:]); err == nil {
   252  						a.log.Debugln(listenaddr[7:], "was cleaned up")
   253  					} else {
   254  						a.log.Errorln(listenaddr[7:], "already exists and was not cleaned up:", err)
   255  						os.Exit(1)
   256  					}
   257  				}
   258  			}
   259  			a.listener, err = net.Listen("unix", listenaddr[7:])
   260  			if err == nil {
   261  				switch listenaddr[7:8] {
   262  				case "@": // maybe abstract namespace
   263  				default:
   264  					if err := os.Chmod(listenaddr[7:], 0660); err != nil {
   265  						a.log.Warnln("WARNING:", listenaddr[:7], "may have unsafe permissions!")
   266  					}
   267  				}
   268  			}
   269  		case "tcp":
   270  			a.listener, err = net.Listen("tcp", u.Host)
   271  		default:
   272  			// err = errors.New(fmt.Sprint("protocol not supported: ", u.Scheme))
   273  			a.listener, err = net.Listen("tcp", listenaddr)
   274  		}
   275  	} else {
   276  		a.listener, err = net.Listen("tcp", listenaddr)
   277  	}
   278  	if err != nil {
   279  		a.log.Errorf("Admin socket failed to listen: %v", err)
   280  		os.Exit(1)
   281  	}
   282  	a.log.Infof("%s admin socket listening on %s",
   283  		strings.ToUpper(a.listener.Addr().Network()),
   284  		a.listener.Addr().String())
   285  	defer a.listener.Close()
   286  	for {
   287  		conn, err := a.listener.Accept()
   288  		if err == nil {
   289  			go a.handleRequest(conn)
   290  		} else {
   291  			select {
   292  			case <-a.done:
   293  				// Not blocked, so we havent started or already stopped
   294  				return
   295  			default:
   296  				// Blocked, so we're supposed to keep running
   297  			}
   298  		}
   299  	}
   300  }
   301  
   302  // handleRequest calls the request handler for each request sent to the admin API.
   303  func (a *AdminSocket) handleRequest(conn net.Conn) {
   304  	decoder := json.NewDecoder(conn)
   305  	decoder.DisallowUnknownFields()
   306  
   307  	encoder := json.NewEncoder(conn)
   308  	encoder.SetIndent("", "  ")
   309  
   310  	defer conn.Close()
   311  
   312  	/*
   313  		defer func() {
   314  			r := recover()
   315  			if r != nil {
   316  				fmt.Println("ERROR:", r)
   317  				a.log.Debugln("Admin socket error:", r)
   318  				if err := encoder.Encode(&ErrorResponse{
   319  					Error: "Check your syntax and input types",
   320  				}); err != nil {
   321  					fmt.Println("ERROR 2:", err)
   322  					a.log.Debugln("Admin socket JSON encode error:", err)
   323  				}
   324  				conn.Close()
   325  			}
   326  		}()
   327  	*/
   328  
   329  	for {
   330  		var err error
   331  		var buf json.RawMessage
   332  		var req AdminSocketRequest
   333  		var resp AdminSocketResponse
   334  		req.Arguments = []byte("{}")
   335  		if err := func() error {
   336  			if err = decoder.Decode(&buf); err != nil {
   337  				return fmt.Errorf("Failed to find request")
   338  			}
   339  			if err = json.Unmarshal(buf, &req); err != nil {
   340  				return fmt.Errorf("Failed to unmarshal request")
   341  			}
   342  			resp.Request = req
   343  			if req.Name == "" {
   344  				return fmt.Errorf("No request specified")
   345  			}
   346  			reqname := strings.ToLower(req.Name)
   347  			handler, ok := a.handlers[reqname]
   348  			if !ok {
   349  				return fmt.Errorf("Unknown action '%s', try 'list' for help", reqname)
   350  			}
   351  			res, err := handler.handler(req.Arguments)
   352  			if err != nil {
   353  				return err
   354  			}
   355  			if resp.Response, err = json.Marshal(res); err != nil {
   356  				return fmt.Errorf("Failed to marshal response: %w", err)
   357  			}
   358  			resp.Status = "success"
   359  			return nil
   360  		}(); err != nil {
   361  			resp.Status = "error"
   362  			resp.Error = err.Error()
   363  		}
   364  		if err = encoder.Encode(resp); err != nil {
   365  			a.log.Debugln("Encode error:", err)
   366  		}
   367  		if !req.KeepAlive {
   368  			break
   369  		} else {
   370  			continue
   371  		}
   372  	}
   373  }
   374  
   375  type DataUnit uint64
   376  
   377  func (d DataUnit) String() string {
   378  	switch {
   379  	case d > 1024*1024*1024*1024:
   380  		return fmt.Sprintf("%2.ftb", float64(d)/1024/1024/1024/1024)
   381  	case d > 1024*1024*1024:
   382  		return fmt.Sprintf("%2.fgb", float64(d)/1024/1024/1024)
   383  	case d > 1024*1024:
   384  		return fmt.Sprintf("%2.fmb", float64(d)/1024/1024)
   385  	default:
   386  		return fmt.Sprintf("%2.fkb", float64(d)/1024)
   387  	}
   388  }