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  }