github.com/palcoin-project/palcd@v1.0.0/btcd.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/palcoin-project/palcd/blockchain/indexers" 19 "github.com/palcoin-project/palcd/database" 20 "github.com/palcoin-project/palcd/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 palcd. 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 if cfg.DropCfIndex { 139 if err := indexers.DropCfIndex(db, interrupt); err != nil { 140 btcdLog.Errorf("%v", err) 141 return err 142 } 143 144 return nil 145 } 146 147 // Create server and start it. 148 server, err := newServer(cfg.Listeners, cfg.AgentBlacklist, 149 cfg.AgentWhitelist, db, activeNetParams.Params, interrupt) 150 if err != nil { 151 // TODO: this logging could do with some beautifying. 152 btcdLog.Errorf("Unable to start server on %v: %v", 153 cfg.Listeners, err) 154 return err 155 } 156 defer func() { 157 btcdLog.Infof("Gracefully shutting down the server...") 158 server.Stop() 159 server.WaitForShutdown() 160 srvrLog.Infof("Server shutdown complete") 161 }() 162 server.Start() 163 if serverChan != nil { 164 serverChan <- server 165 } 166 167 // Wait until the interrupt signal is received from an OS signal or 168 // shutdown is requested through one of the subsystems such as the RPC 169 // server. 170 <-interrupt 171 return nil 172 } 173 174 // removeRegressionDB removes the existing regression test database if running 175 // in regression test mode and it already exists. 176 func removeRegressionDB(dbPath string) error { 177 // Don't do anything if not in regression test mode. 178 if !cfg.RegressionTest { 179 return nil 180 } 181 182 // Remove the old regression test database if it already exists. 183 fi, err := os.Stat(dbPath) 184 if err == nil { 185 btcdLog.Infof("Removing regression test database from '%s'", dbPath) 186 if fi.IsDir() { 187 err := os.RemoveAll(dbPath) 188 if err != nil { 189 return err 190 } 191 } else { 192 err := os.Remove(dbPath) 193 if err != nil { 194 return err 195 } 196 } 197 } 198 199 return nil 200 } 201 202 // dbPath returns the path to the block database given a database type. 203 func blockDbPath(dbType string) string { 204 // The database name is based on the database type. 205 dbName := blockDbNamePrefix + "_" + dbType 206 if dbType == "sqlite" { 207 dbName = dbName + ".db" 208 } 209 dbPath := filepath.Join(cfg.DataDir, dbName) 210 return dbPath 211 } 212 213 // warnMultipleDBs shows a warning if multiple block database types are detected. 214 // This is not a situation most users want. It is handy for development however 215 // to support multiple side-by-side databases. 216 func warnMultipleDBs() { 217 // This is intentionally not using the known db types which depend 218 // on the database types compiled into the binary since we want to 219 // detect legacy db types as well. 220 dbTypes := []string{"ffldb", "leveldb", "sqlite"} 221 duplicateDbPaths := make([]string, 0, len(dbTypes)-1) 222 for _, dbType := range dbTypes { 223 if dbType == cfg.DbType { 224 continue 225 } 226 227 // Store db path as a duplicate db if it exists. 228 dbPath := blockDbPath(dbType) 229 if fileExists(dbPath) { 230 duplicateDbPaths = append(duplicateDbPaths, dbPath) 231 } 232 } 233 234 // Warn if there are extra databases. 235 if len(duplicateDbPaths) > 0 { 236 selectedDbPath := blockDbPath(cfg.DbType) 237 btcdLog.Warnf("WARNING: There are multiple block chain databases "+ 238 "using different database types.\nYou probably don't "+ 239 "want to waste disk space by having more than one.\n"+ 240 "Your current database is located at [%v].\nThe "+ 241 "additional database is located at %v", selectedDbPath, 242 duplicateDbPaths) 243 } 244 } 245 246 // loadBlockDB loads (or creates when needed) the block database taking into 247 // account the selected database backend and returns a handle to it. It also 248 // contains additional logic such warning the user if there are multiple 249 // databases which consume space on the file system and ensuring the regression 250 // test database is clean when in regression test mode. 251 func loadBlockDB() (database.DB, error) { 252 // The memdb backend does not have a file path associated with it, so 253 // handle it uniquely. We also don't want to worry about the multiple 254 // database type warnings when running with the memory database. 255 if cfg.DbType == "memdb" { 256 btcdLog.Infof("Creating block database in memory.") 257 db, err := database.Create(cfg.DbType) 258 if err != nil { 259 return nil, err 260 } 261 return db, nil 262 } 263 264 warnMultipleDBs() 265 266 // The database name is based on the database type. 267 dbPath := blockDbPath(cfg.DbType) 268 269 // The regression test is special in that it needs a clean database for 270 // each run, so remove it now if it already exists. 271 removeRegressionDB(dbPath) 272 273 btcdLog.Infof("Loading block database from '%s'", dbPath) 274 db, err := database.Open(cfg.DbType, dbPath, activeNetParams.Net) 275 if err != nil { 276 // Return the error if it's not because the database doesn't 277 // exist. 278 if dbErr, ok := err.(database.Error); !ok || dbErr.ErrorCode != 279 database.ErrDbDoesNotExist { 280 281 return nil, err 282 } 283 284 // Create the db if it does not exist. 285 err = os.MkdirAll(cfg.DataDir, 0700) 286 if err != nil { 287 return nil, err 288 } 289 db, err = database.Create(cfg.DbType, dbPath, activeNetParams.Net) 290 if err != nil { 291 return nil, err 292 } 293 } 294 295 btcdLog.Info("Block database loaded") 296 return db, nil 297 } 298 299 func main() { 300 // Block and transaction processing can cause bursty allocations. This 301 // limits the garbage collector from excessively overallocating during 302 // bursts. This value was arrived at with the help of profiling live 303 // usage. 304 debug.SetGCPercent(10) 305 306 // Up some limits. 307 if err := limits.SetLimits(); err != nil { 308 fmt.Fprintf(os.Stderr, "failed to set limits: %v\n", err) 309 os.Exit(1) 310 } 311 312 // Call serviceMain on Windows to handle running as a service. When 313 // the return isService flag is true, exit now since we ran as a 314 // service. Otherwise, just fall through to normal operation. 315 if runtime.GOOS == "windows" { 316 isService, err := winServiceMain() 317 if err != nil { 318 fmt.Println(err) 319 os.Exit(1) 320 } 321 if isService { 322 os.Exit(0) 323 } 324 } 325 326 // Work around defer not working after os.Exit() 327 if err := btcdMain(nil); err != nil { 328 os.Exit(1) 329 } 330 }