176 lines
4.8 KiB
Go
Raw Normal View History

2021-05-23 14:42:26 -05:00
package core
2017-12-28 22:16:20 -06:00
2018-06-12 17:50:08 -05:00
import (
"context"
"crypto/ed25519"
2018-06-12 17:50:08 -05:00
"encoding/hex"
"errors"
2018-06-12 17:50:08 -05:00
"io/ioutil"
2021-05-23 21:47:12 -05:00
"net/url"
"time"
2018-06-12 17:50:08 -05:00
iw "github.com/Arceliar/ironwood/encrypted"
"github.com/Arceliar/phony"
"github.com/gologme/log"
2018-12-07 19:56:04 -06:00
"github.com/yggdrasil-network/yggdrasil-go/src/config"
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/version"
2018-06-12 17:50:08 -05:00
)
2017-12-28 22:16:20 -06:00
2018-05-27 22:13:37 +01:00
// The Core object represents the Yggdrasil node. You should create a Core
// object for each Yggdrasil node you plan to run.
2017-12-28 22:16:20 -06:00
type Core struct {
2018-01-04 22:37:51 +00:00
// This is the main data structure that holds everything else for a node
// We're going to keep our own copy of the provided config - that way we can
// guarantee that it will be covered by the mutex
2019-08-28 19:31:04 +01:00
phony.Inbox
*iw.PacketConn
config *config.NodeConfig // Config
secret ed25519.PrivateKey
public ed25519.PublicKey
2020-05-23 10:23:55 -05:00
links links
log *log.Logger
addPeerTimer *time.Timer
ctx context.Context
ctxCancel context.CancelFunc
2017-12-28 22:16:20 -06:00
}
2019-08-28 19:53:52 +01:00
func (c *Core) _init() error {
2018-01-04 22:37:51 +00:00
// TODO separate init and start functions
// Init sets up structs
// Start launches goroutines that depend on structs being set up
// This is pretty much required to completely avoid race conditions
2018-05-27 22:13:37 +01:00
if c.log == nil {
c.log = log.New(ioutil.Discard, "", 0)
}
2018-12-29 19:14:26 +00:00
c.config.RLock()
sigPriv, err := hex.DecodeString(c.config.PrivateKey)
c.config.RUnlock()
2018-12-29 19:14:26 +00:00
if err != nil {
return err
}
if len(sigPriv) < ed25519.PrivateKeySize {
return errors.New("PrivateKey is incorrect length")
}
2018-12-29 19:14:26 +00:00
c.secret = ed25519.PrivateKey(sigPriv)
c.public = c.secret.Public().(ed25519.PublicKey)
// TODO check public against current.PublicKey, error if they don't match
c.PacketConn, err = iw.NewPacketConn(c.secret)
c.ctx, c.ctxCancel = context.WithCancel(context.Background())
return err
2017-12-28 22:16:20 -06:00
}
// If any static peers were provided in the configuration above then we should
// configure them. The loop ensures that disconnected peers will eventually
// be reconnected with.
2019-08-28 19:53:52 +01:00
func (c *Core) _addPeerLoop() {
c.config.RLock()
defer c.config.RUnlock()
if c.addPeerTimer == nil {
return
}
2019-08-28 19:53:52 +01:00
// Add peers from the Peers section
for _, peer := range c.config.Peers {
go func(peer string, intf string) {
2021-05-23 21:47:12 -05:00
u, err := url.Parse(peer)
if err != nil {
c.log.Errorln("Failed to parse peer url:", peer, err)
}
if err := c.CallPeer(u, intf); err != nil {
c.log.Errorln("Failed to add peer:", err)
}
2019-09-18 16:15:33 +01:00
}(peer, "") // TODO: this should be acted and not in a goroutine?
2019-08-28 19:53:52 +01:00
}
2019-08-28 19:53:52 +01:00
// Add peers from the InterfacePeers section
for intf, intfpeers := range c.config.InterfacePeers {
2019-08-28 19:53:52 +01:00
for _, peer := range intfpeers {
go func(peer string, intf string) {
2021-05-23 21:47:12 -05:00
u, err := url.Parse(peer)
if err != nil {
c.log.Errorln("Failed to parse peer url:", peer, err)
}
if err := c.CallPeer(u, intf); err != nil {
c.log.Errorln("Failed to add peer:", err)
}
2019-09-18 16:15:33 +01:00
}(peer, intf) // TODO: this should be acted and not in a goroutine?
2019-08-28 19:53:52 +01:00
}
}
2019-08-28 19:53:52 +01:00
c.addPeerTimer = time.AfterFunc(time.Minute, func() {
c.Act(nil, c._addPeerLoop)
})
}
// Start starts up Yggdrasil using the provided config.NodeConfig, and outputs
// debug logging through the provided log.Logger. The started stack will include
// TCP and UDP sockets, a multicast discovery socket, an admin socket, router,
// switch and DHT node. A config.NodeState is returned which contains both the
// current and previous configurations (from reconfigures).
func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) (err error) {
2019-08-28 19:53:52 +01:00
phony.Block(c, func() {
err = c._start(nc, log)
2019-08-28 19:53:52 +01:00
})
return
}
// This function is unsafe and should only be ran by the core actor.
func (c *Core) _start(nc *config.NodeConfig, log *log.Logger) error {
2018-05-27 22:13:37 +01:00
c.log = log
c.config = nc
if name := version.BuildName(); name != "unknown" {
c.log.Infoln("Build name:", name)
}
if version := version.BuildVersion(); version != "unknown" {
c.log.Infoln("Build version:", version)
}
c.log.Infoln("Starting up...")
if err := c._init(); err != nil {
c.log.Errorln("Failed to initialize core")
return err
}
2018-05-27 22:13:37 +01:00
2020-05-23 10:23:55 -05:00
if err := c.links.init(c); err != nil {
c.log.Errorln("Failed to start link interfaces")
return err
}
c.addPeerTimer = time.AfterFunc(0, func() {
c.Act(nil, c._addPeerLoop)
})
c.log.Infoln("Startup complete")
return nil
2018-05-27 22:13:37 +01:00
}
// Stop shuts down the Yggdrasil node.
2018-05-27 22:13:37 +01:00
func (c *Core) Stop() {
2019-08-28 19:53:52 +01:00
phony.Block(c, c._stop)
}
// This function is unsafe and should only be ran by the core actor.
func (c *Core) _stop() {
2021-06-12 06:07:33 -05:00
c.log.Infoln("Stopping...")
c.ctxCancel()
c.PacketConn.Close()
2019-09-18 15:34:26 +01:00
if c.addPeerTimer != nil {
c.addPeerTimer.Stop()
c.addPeerTimer = nil
2019-09-18 15:34:26 +01:00
}
2021-06-02 14:40:09 +01:00
_ = c.links.stop()
2020-03-29 00:48:41 -05:00
/* FIXME this deadlocks, need a waitgroup or something to coordinate shutdown
for _, peer := range c.GetPeers() {
c.DisconnectPeer(peer.Port)
}
2020-03-29 00:48:41 -05:00
*/
2019-09-18 16:32:22 +01:00
c.log.Infoln("Stopped")
2018-05-27 22:13:37 +01:00
}