/ vendor / github.com / btcsuite / btcd / btcd.go
btcd.go
  1  // Copyright (c) 2013-2016 The btcsuite developers
  2  // Use of this source code is governed by an ISC
  3  // license that can be found in the LICENSE file.
  4  
  5  package main
  6  
  7  import (
  8  	"fmt"
  9  	"net"
 10  	"net/http"
 11  	_ "net/http/pprof"
 12  	"os"
 13  	"path/filepath"
 14  	"runtime"
 15  	"runtime/debug"
 16  	"runtime/pprof"
 17  
 18  	"github.com/btcsuite/btcd/blockchain/indexers"
 19  	"github.com/btcsuite/btcd/database"
 20  	"github.com/btcsuite/btcd/limits"
 21  )
 22  
 23  const (
 24  	// blockDbNamePrefix is the prefix for the block database name.  The
 25  	// database type is appended to this value to form the full block
 26  	// database name.
 27  	blockDbNamePrefix = "blocks"
 28  )
 29  
 30  var (
 31  	cfg *config
 32  )
 33  
 34  // winServiceMain is only invoked on Windows.  It detects when btcd is running
 35  // as a service and reacts accordingly.
 36  var winServiceMain func() (bool, error)
 37  
 38  // btcdMain is the real main function for btcd.  It is necessary to work around
 39  // the fact that deferred functions do not run when os.Exit() is called.  The
 40  // optional serverChan parameter is mainly used by the service code to be
 41  // notified with the server once it is setup so it can gracefully stop it when
 42  // requested from the service control manager.
 43  func btcdMain(serverChan chan<- *server) error {
 44  	// Load configuration and parse command line.  This function also
 45  	// initializes logging and configures it accordingly.
 46  	tcfg, _, err := loadConfig()
 47  	if err != nil {
 48  		return err
 49  	}
 50  	cfg = tcfg
 51  	defer func() {
 52  		if logRotator != nil {
 53  			logRotator.Close()
 54  		}
 55  	}()
 56  
 57  	// Get a channel that will be closed when a shutdown signal has been
 58  	// triggered either from an OS signal such as SIGINT (Ctrl+C) or from
 59  	// another subsystem such as the RPC server.
 60  	interrupt := interruptListener()
 61  	defer btcdLog.Info("Shutdown complete")
 62  
 63  	// Show version at startup.
 64  	btcdLog.Infof("Version %s", version())
 65  
 66  	// Enable http profiling server if requested.
 67  	if cfg.Profile != "" {
 68  		go func() {
 69  			listenAddr := net.JoinHostPort("", cfg.Profile)
 70  			btcdLog.Infof("Profile server listening on %s", listenAddr)
 71  			profileRedirect := http.RedirectHandler("/debug/pprof",
 72  				http.StatusSeeOther)
 73  			http.Handle("/", profileRedirect)
 74  			btcdLog.Errorf("%v", http.ListenAndServe(listenAddr, nil))
 75  		}()
 76  	}
 77  
 78  	// Write cpu profile if requested.
 79  	if cfg.CPUProfile != "" {
 80  		f, err := os.Create(cfg.CPUProfile)
 81  		if err != nil {
 82  			btcdLog.Errorf("Unable to create cpu profile: %v", err)
 83  			return err
 84  		}
 85  		pprof.StartCPUProfile(f)
 86  		defer f.Close()
 87  		defer pprof.StopCPUProfile()
 88  	}
 89  
 90  	// Perform upgrades to btcd as new versions require it.
 91  	if err := doUpgrades(); err != nil {
 92  		btcdLog.Errorf("%v", err)
 93  		return err
 94  	}
 95  
 96  	// Return now if an interrupt signal was triggered.
 97  	if interruptRequested(interrupt) {
 98  		return nil
 99  	}
100  
101  	// Load the block database.
102  	db, err := loadBlockDB()
103  	if err != nil {
104  		btcdLog.Errorf("%v", err)
105  		return err
106  	}
107  	defer func() {
108  		// Ensure the database is sync'd and closed on shutdown.
109  		btcdLog.Infof("Gracefully shutting down the database...")
110  		db.Close()
111  	}()
112  
113  	// Return now if an interrupt signal was triggered.
114  	if interruptRequested(interrupt) {
115  		return nil
116  	}
117  
118  	// Drop indexes and exit if requested.
119  	//
120  	// NOTE: The order is important here because dropping the tx index also
121  	// drops the address index since it relies on it.
122  	if cfg.DropAddrIndex {
123  		if err := indexers.DropAddrIndex(db, interrupt); err != nil {
124  			btcdLog.Errorf("%v", err)
125  			return err
126  		}
127  
128  		return nil
129  	}
130  	if cfg.DropTxIndex {
131  		if err := indexers.DropTxIndex(db, interrupt); err != nil {
132  			btcdLog.Errorf("%v", err)
133  			return err
134  		}
135  
136  		return nil
137  	}
138  
139  	// Create server and start it.
140  	server, err := newServer(cfg.Listeners, db, activeNetParams.Params,
141  		interrupt)
142  	if err != nil {
143  		// TODO: this logging could do with some beautifying.
144  		btcdLog.Errorf("Unable to start server on %v: %v",
145  			cfg.Listeners, err)
146  		return err
147  	}
148  	defer func() {
149  		btcdLog.Infof("Gracefully shutting down the server...")
150  		server.Stop()
151  		server.WaitForShutdown()
152  		srvrLog.Infof("Server shutdown complete")
153  	}()
154  	server.Start()
155  	if serverChan != nil {
156  		serverChan <- server
157  	}
158  
159  	// Wait until the interrupt signal is received from an OS signal or
160  	// shutdown is requested through one of the subsystems such as the RPC
161  	// server.
162  	<-interrupt
163  	return nil
164  }
165  
166  // removeRegressionDB removes the existing regression test database if running
167  // in regression test mode and it already exists.
168  func removeRegressionDB(dbPath string) error {
169  	// Don't do anything if not in regression test mode.
170  	if !cfg.RegressionTest {
171  		return nil
172  	}
173  
174  	// Remove the old regression test database if it already exists.
175  	fi, err := os.Stat(dbPath)
176  	if err == nil {
177  		btcdLog.Infof("Removing regression test database from '%s'", dbPath)
178  		if fi.IsDir() {
179  			err := os.RemoveAll(dbPath)
180  			if err != nil {
181  				return err
182  			}
183  		} else {
184  			err := os.Remove(dbPath)
185  			if err != nil {
186  				return err
187  			}
188  		}
189  	}
190  
191  	return nil
192  }
193  
194  // dbPath returns the path to the block database given a database type.
195  func blockDbPath(dbType string) string {
196  	// The database name is based on the database type.
197  	dbName := blockDbNamePrefix + "_" + dbType
198  	if dbType == "sqlite" {
199  		dbName = dbName + ".db"
200  	}
201  	dbPath := filepath.Join(cfg.DataDir, dbName)
202  	return dbPath
203  }
204  
205  // warnMultipeDBs shows a warning if multiple block database types are detected.
206  // This is not a situation most users want.  It is handy for development however
207  // to support multiple side-by-side databases.
208  func warnMultipeDBs() {
209  	// This is intentionally not using the known db types which depend
210  	// on the database types compiled into the binary since we want to
211  	// detect legacy db types as well.
212  	dbTypes := []string{"ffldb", "leveldb", "sqlite"}
213  	duplicateDbPaths := make([]string, 0, len(dbTypes)-1)
214  	for _, dbType := range dbTypes {
215  		if dbType == cfg.DbType {
216  			continue
217  		}
218  
219  		// Store db path as a duplicate db if it exists.
220  		dbPath := blockDbPath(dbType)
221  		if fileExists(dbPath) {
222  			duplicateDbPaths = append(duplicateDbPaths, dbPath)
223  		}
224  	}
225  
226  	// Warn if there are extra databases.
227  	if len(duplicateDbPaths) > 0 {
228  		selectedDbPath := blockDbPath(cfg.DbType)
229  		btcdLog.Warnf("WARNING: There are multiple block chain databases "+
230  			"using different database types.\nYou probably don't "+
231  			"want to waste disk space by having more than one.\n"+
232  			"Your current database is located at [%v].\nThe "+
233  			"additional database is located at %v", selectedDbPath,
234  			duplicateDbPaths)
235  	}
236  }
237  
238  // loadBlockDB loads (or creates when needed) the block database taking into
239  // account the selected database backend and returns a handle to it.  It also
240  // contains additional logic such warning the user if there are multiple
241  // databases which consume space on the file system and ensuring the regression
242  // test database is clean when in regression test mode.
243  func loadBlockDB() (database.DB, error) {
244  	// The memdb backend does not have a file path associated with it, so
245  	// handle it uniquely.  We also don't want to worry about the multiple
246  	// database type warnings when running with the memory database.
247  	if cfg.DbType == "memdb" {
248  		btcdLog.Infof("Creating block database in memory.")
249  		db, err := database.Create(cfg.DbType)
250  		if err != nil {
251  			return nil, err
252  		}
253  		return db, nil
254  	}
255  
256  	warnMultipeDBs()
257  
258  	// The database name is based on the database type.
259  	dbPath := blockDbPath(cfg.DbType)
260  
261  	// The regression test is special in that it needs a clean database for
262  	// each run, so remove it now if it already exists.
263  	removeRegressionDB(dbPath)
264  
265  	btcdLog.Infof("Loading block database from '%s'", dbPath)
266  	db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net)
267  	if err != nil {
268  		// Return the error if it's not because the database doesn't
269  		// exist.
270  		if dbErr, ok := err.(database.Error); !ok || dbErr.ErrorCode !=
271  			database.ErrDbDoesNotExist {
272  
273  			return nil, err
274  		}
275  
276  		// Create the db if it does not exist.
277  		err = os.MkdirAll(cfg.DataDir, 0700)
278  		if err != nil {
279  			return nil, err
280  		}
281  		db, err = database.Create(cfg.DbType, dbPath, activeNetParams.Net)
282  		if err != nil {
283  			return nil, err
284  		}
285  	}
286  
287  	btcdLog.Info("Block database loaded")
288  	return db, nil
289  }
290  
291  func main() {
292  	// Use all processor cores.
293  	runtime.GOMAXPROCS(runtime.NumCPU())
294  
295  	// Block and transaction processing can cause bursty allocations.  This
296  	// limits the garbage collector from excessively overallocating during
297  	// bursts.  This value was arrived at with the help of profiling live
298  	// usage.
299  	debug.SetGCPercent(10)
300  
301  	// Up some limits.
302  	if err := limits.SetLimits(); err != nil {
303  		fmt.Fprintf(os.Stderr, "failed to set limits: %v\n", err)
304  		os.Exit(1)
305  	}
306  
307  	// Call serviceMain on Windows to handle running as a service.  When
308  	// the return isService flag is true, exit now since we ran as a
309  	// service.  Otherwise, just fall through to normal operation.
310  	if runtime.GOOS == "windows" {
311  		isService, err := winServiceMain()
312  		if err != nil {
313  			fmt.Println(err)
314  			os.Exit(1)
315  		}
316  		if isService {
317  			os.Exit(0)
318  		}
319  	}
320  
321  	// Work around defer not working after os.Exit()
322  	if err := btcdMain(nil); err != nil {
323  		os.Exit(1)
324  	}
325  }