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 }