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)