github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/gateway/gateway.go (about)

     1  package gateway
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"os"
     8  	"path/filepath"
     9  	"time"
    10  
    11  	"github.com/NebulousLabs/Sia/build"
    12  	"github.com/NebulousLabs/Sia/modules"
    13  	"github.com/NebulousLabs/Sia/persist"
    14  	"github.com/NebulousLabs/Sia/sync"
    15  )
    16  
    17  var (
    18  	errNoPeers     = errors.New("no peers")
    19  	errUnreachable = errors.New("peer did not respond to ping")
    20  )
    21  
    22  // Gateway implements the modules.Gateway interface.
    23  type Gateway struct {
    24  	listener net.Listener
    25  	myAddr   modules.NetAddress
    26  
    27  	// handlers are the RPCs that the Gateway can handle.
    28  	handlers map[rpcID]modules.RPCFunc
    29  	// initRPCs are the RPCs that the Gateway calls upon connecting to a peer.
    30  	initRPCs map[string]modules.RPCFunc
    31  
    32  	// peers are the nodes we are currently connected to.
    33  	peers map[modules.NetAddress]*peer
    34  
    35  	// nodes is the set of all known nodes (i.e. potential peers) on the
    36  	// network.
    37  	nodes map[modules.NetAddress]struct{}
    38  
    39  	// closeChan is used to shut down the Gateway's goroutines.
    40  	closeChan chan struct{}
    41  
    42  	persistDir string
    43  
    44  	log *persist.Logger
    45  	mu  *sync.RWMutex
    46  }
    47  
    48  // Address returns the NetAddress of the Gateway.
    49  func (g *Gateway) Address() modules.NetAddress {
    50  	id := g.mu.RLock()
    51  	defer g.mu.RUnlock(id)
    52  	return g.myAddr
    53  }
    54  
    55  // Close saves the state of the Gateway and stops its listener process.
    56  func (g *Gateway) Close() error {
    57  	var errs []error
    58  
    59  	// save the latest gateway state
    60  	id := g.mu.RLock()
    61  	if err := g.saveSync(); err != nil {
    62  		errs = append(errs, fmt.Errorf("save failed: %v", err))
    63  	}
    64  	g.mu.RUnlock(id)
    65  	// send close signal
    66  	close(g.closeChan)
    67  	// clear the port mapping (no effect if UPnP not supported)
    68  	id = g.mu.RLock()
    69  	g.clearPort(g.myAddr.Port())
    70  	g.mu.RUnlock(id)
    71  	// shut down the listener
    72  	if err := g.listener.Close(); err != nil {
    73  		errs = append(errs, fmt.Errorf("listener.Close failed: %v", err))
    74  	}
    75  	// Disconnect from peers.
    76  	for _, p := range g.Peers() {
    77  		if err := g.Disconnect(p.NetAddress); err != nil {
    78  			errs = append(errs, fmt.Errorf("Disconnect failed: %v", err))
    79  		}
    80  	}
    81  	// Sleep to give time for all goroutines to exit. This is necessary because
    82  	// some goroutines write to the logger so we must give them time to exit
    83  	// before closing the logger.
    84  	// TODO: block until goroutines exit instead of sleeping.
    85  	time.Sleep(100 * time.Millisecond)
    86  	// Close the logger. The logger should be the last thing to shut down so that
    87  	// all other objects have access to logging while closing.
    88  	if err := g.log.Close(); err != nil {
    89  		errs = append(errs, fmt.Errorf("log.Close failed: %v", err))
    90  	}
    91  
    92  	return build.JoinErrors(errs, "; ")
    93  }
    94  
    95  // New returns an initialized Gateway.
    96  func New(addr string, persistDir string) (g *Gateway, err error) {
    97  	// Create the directory if it doesn't exist.
    98  	err = os.MkdirAll(persistDir, 0700)
    99  	if err != nil {
   100  		return
   101  	}
   102  
   103  	g = &Gateway{
   104  		handlers:   make(map[rpcID]modules.RPCFunc),
   105  		initRPCs:   make(map[string]modules.RPCFunc),
   106  		peers:      make(map[modules.NetAddress]*peer),
   107  		nodes:      make(map[modules.NetAddress]struct{}),
   108  		closeChan:  make(chan struct{}),
   109  		persistDir: persistDir,
   110  		mu:         sync.New(modules.SafeMutexDelay, 2),
   111  	}
   112  
   113  	// Create the logger.
   114  	g.log, err = persist.NewFileLogger(filepath.Join(g.persistDir, logFile))
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	// Register RPCs.
   120  	g.RegisterRPC("ShareNodes", g.shareNodes)
   121  	g.RegisterRPC("RelayNode", g.relayNode)
   122  	g.RegisterConnectCall("ShareNodes", g.requestNodes)
   123  
   124  	// Load the old node list. If it doesn't exist, no problem, but if it does,
   125  	// we want to know about any errors preventing us from loading it.
   126  	if loadErr := g.load(); loadErr != nil && !os.IsNotExist(loadErr) {
   127  		return nil, loadErr
   128  	}
   129  
   130  	// Add the bootstrap peers to the node list.
   131  	if build.Release == "standard" {
   132  		for _, addr := range modules.BootstrapPeers {
   133  			err := g.addNode(addr)
   134  			if err != nil {
   135  				g.log.Printf("WARN: failed to add the bootstrap node '%v': %v", addr, err)
   136  			}
   137  		}
   138  		g.save()
   139  	}
   140  
   141  	// Create listener and set address.
   142  	g.listener, err = net.Listen("tcp", addr)
   143  	if err != nil {
   144  		return
   145  	}
   146  	_, port, portErr := net.SplitHostPort(g.listener.Addr().String())
   147  	if portErr != nil {
   148  		return nil, portErr
   149  	}
   150  	if build.Release == "testing" {
   151  		g.myAddr = modules.NetAddress(g.listener.Addr().String())
   152  	}
   153  
   154  	g.log.Println("INFO: gateway created, started logging")
   155  
   156  	// Forward the RPC port, if possible.
   157  	go g.forwardPort(port)
   158  
   159  	// Learn our external IP.
   160  	go g.learnHostname(port)
   161  
   162  	// Spawn the peer and node managers. These will attempt to keep the peer
   163  	// and node lists healthy.
   164  	go g.threadedPeerManager()
   165  	go g.threadedNodeManager()
   166  
   167  	// Spawn the primary listener.
   168  	go g.listen()
   169  
   170  	return
   171  }
   172  
   173  // enforce that Gateway satisfies the modules.Gateway interface
   174  var _ modules.Gateway = (*Gateway)(nil)