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