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.
18 "github.com/btcsuite/btcd/blockchain/indexers"
19 "github.com/btcsuite/btcd/database"
20 "github.com/btcsuite/btcd/limits"
24 // blockDbNamePrefix is the prefix for the block database name. The
25 // database type is appended to this value to form the full block
27 blockDbNamePrefix = "blocks"
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)
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()
52 if logRotator != nil {
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 interruptedChan := interruptListener()
61 defer btcdLog.Info("Shutdown complete")
63 // Show version at startup.
64 btcdLog.Infof("Version %s", version())
66 // Enable http profiling server if requested.
67 if cfg.Profile != "" {
69 listenAddr := net.JoinHostPort("", cfg.Profile)
70 btcdLog.Infof("Profile server listening on %s", listenAddr)
71 profileRedirect := http.RedirectHandler("/debug/pprof",
73 http.Handle("/", profileRedirect)
74 btcdLog.Errorf("%v", http.ListenAndServe(listenAddr, nil))
78 // Write cpu profile if requested.
79 if cfg.CPUProfile != "" {
80 f, err := os.Create(cfg.CPUProfile)
82 btcdLog.Errorf("Unable to create cpu profile: %v", err)
85 pprof.StartCPUProfile(f)
87 defer pprof.StopCPUProfile()
90 // Perform upgrades to btcd as new versions require it.
91 if err := doUpgrades(); err != nil {
92 btcdLog.Errorf("%v", err)
96 // Return now if an interrupt signal was triggered.
97 if interruptRequested(interruptedChan) {
101 // Load the block database.
102 db, err := loadBlockDB()
104 btcdLog.Errorf("%v", err)
108 // Ensure the database is sync'd and closed on shutdown.
109 btcdLog.Infof("Gracefully shutting down the database...")
113 // Return now if an interrupt signal was triggered.
114 if interruptRequested(interruptedChan) {
118 // Drop indexes and exit if requested.
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); err != nil {
124 btcdLog.Errorf("%v", err)
131 if err := indexers.DropTxIndex(db); err != nil {
132 btcdLog.Errorf("%v", err)
139 // Create server and start it.
140 server, err := newServer(cfg.Listeners, db, activeNetParams.Params)
142 // TODO: this logging could do with some beautifying.
143 btcdLog.Errorf("Unable to start server on %v: %v",
148 btcdLog.Infof("Gracefully shutting down the server...")
150 server.WaitForShutdown()
151 srvrLog.Infof("Server shutdown complete")
154 if serverChan != nil {
158 // Wait until the interrupt signal is received from an OS signal or
159 // shutdown is requested through one of the subsystems such as the RPC
165 // removeRegressionDB removes the existing regression test database if running
166 // in regression test mode and it already exists.
167 func removeRegressionDB(dbPath string) error {
168 // Don't do anything if not in regression test mode.
169 if !cfg.RegressionTest {
173 // Remove the old regression test database if it already exists.
174 fi, err := os.Stat(dbPath)
176 btcdLog.Infof("Removing regression test database from '%s'", dbPath)
178 err := os.RemoveAll(dbPath)
183 err := os.Remove(dbPath)
193 // dbPath returns the path to the block database given a database type.
194 func blockDbPath(dbType string) string {
195 // The database name is based on the database type.
196 dbName := blockDbNamePrefix + "_" + dbType
197 if dbType == "sqlite" {
198 dbName = dbName + ".db"
200 dbPath := filepath.Join(cfg.DataDir, dbName)
204 // warnMultipeDBs shows a warning if multiple block database types are detected.
205 // This is not a situation most users want. It is handy for development however
206 // to support multiple side-by-side databases.
207 func warnMultipeDBs() {
208 // This is intentionally not using the known db types which depend
209 // on the database types compiled into the binary since we want to
210 // detect legacy db types as well.
211 dbTypes := []string{"ffldb", "leveldb", "sqlite"}
212 duplicateDbPaths := make([]string, 0, len(dbTypes)-1)
213 for _, dbType := range dbTypes {
214 if dbType == cfg.DbType {
218 // Store db path as a duplicate db if it exists.
219 dbPath := blockDbPath(dbType)
220 if fileExists(dbPath) {
221 duplicateDbPaths = append(duplicateDbPaths, dbPath)
225 // Warn if there are extra databases.
226 if len(duplicateDbPaths) > 0 {
227 selectedDbPath := blockDbPath(cfg.DbType)
228 btcdLog.Warnf("WARNING: There are multiple block chain databases "+
229 "using different database types.\nYou probably don't "+
230 "want to waste disk space by having more than one.\n"+
231 "Your current database is located at [%v].\nThe "+
232 "additional database is located at %v", selectedDbPath,
237 // loadBlockDB loads (or creates when needed) the block database taking into
238 // account the selected database backend and returns a handle to it. It also
239 // contains additional logic such warning the user if there are multiple
240 // databases which consume space on the file system and ensuring the regression
241 // test database is clean when in regression test mode.
242 func loadBlockDB() (database.DB, error) {
243 // The memdb backend does not have a file path associated with it, so
244 // handle it uniquely. We also don't want to worry about the multiple
245 // database type warnings when running with the memory database.
246 if cfg.DbType == "memdb" {
247 btcdLog.Infof("Creating block database in memory.")
248 db, err := database.Create(cfg.DbType)
257 // The database name is based on the database type.
258 dbPath := blockDbPath(cfg.DbType)
260 // The regression test is special in that it needs a clean database for
261 // each run, so remove it now if it already exists.
262 removeRegressionDB(dbPath)
264 btcdLog.Infof("Loading block database from '%s'", dbPath)
265 db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net)
267 // Return the error if it's not because the database doesn't
269 if dbErr, ok := err.(database.Error); !ok || dbErr.ErrorCode !=
270 database.ErrDbDoesNotExist {
275 // Create the db if it does not exist.
276 err = os.MkdirAll(cfg.DataDir, 0700)
280 db, err = database.Create(cfg.DbType, dbPath, activeNetParams.Net)
286 btcdLog.Info("Block database loaded")
291 // Use all processor cores.
292 runtime.GOMAXPROCS(runtime.NumCPU())
294 // Block and transaction processing can cause bursty allocations. This
295 // limits the garbage collector from excessively overallocating during
296 // bursts. This value was arrived at with the help of profiling live
298 debug.SetGCPercent(10)
301 if err := limits.SetLimits(); err != nil {
302 fmt.Fprintf(os.Stderr, "failed to set limits: %v\n", err)
306 // Call serviceMain on Windows to handle running as a service. When
307 // the return isService flag is true, exit now since we ran as a
308 // service. Otherwise, just fall through to normal operation.
309 if runtime.GOOS == "windows" {
310 isService, err := winServiceMain()
320 // Work around defer not working after os.Exit()
321 if err := btcdMain(nil); err != nil {