// newConfigParser returns a new command line flags parser. func newConfigParser(cfg *config, so *serviceOptions, options flags.Options) *flags.Parser { parser := flags.NewParser(cfg, options) if runtime.GOOS == "windows" { parser.AddGroup("Service Options", "Service Options", so) } return parser }
func GetBtcdConfig() (cfg config, err error) { btcdHomeDir := btcutil.AppDataDir("btcd", false) defaultConfigFile := filepath.Join(btcdHomeDir, "btcd.conf") cfg = config{ ConfigFile: defaultConfigFile, } parser := flags.NewParser(&cfg, flags.Default) err = flags.NewIniParser(parser).ParseFile(cfg.ConfigFile) return }
func main() { cfg := config{ Years: 10, Organization: "gencerts", } parser := flags.NewParser(&cfg, flags.Default) _, err := parser.Parse() if err != nil { if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { parser.WriteHelp(os.Stderr) } return } if cfg.Directory == "" { var err error cfg.Directory, err = os.Getwd() if err != nil { fmt.Fprintf(os.Stderr, "no directory specified and cannot get working directory\n") os.Exit(1) } } cfg.Directory = cleanAndExpandPath(cfg.Directory) certFile := filepath.Join(cfg.Directory, "rpc.cert") keyFile := filepath.Join(cfg.Directory, "rpc.key") if !cfg.Force { if fileExists(certFile) || fileExists(keyFile) { fmt.Fprintf(os.Stderr, "%v: certificate and/or key files exist; use -f to force\n", cfg.Directory) os.Exit(1) } } validUntil := time.Now().Add(time.Duration(cfg.Years) * 365 * 24 * time.Hour) cert, key, err := btcutil.NewTLSCertPair(cfg.Organization, validUntil, cfg.ExtraHosts) if err != nil { fmt.Fprintf(os.Stderr, "cannot generate certificate pair: %v\n", err) os.Exit(1) } // Write cert and key files. if err = ioutil.WriteFile(certFile, cert, 0666); err != nil { fmt.Fprintf(os.Stderr, "cannot write cert: %v\n", err) os.Exit(1) } if err = ioutil.WriteFile(keyFile, key, 0600); err != nil { os.Remove(certFile) fmt.Fprintf(os.Stderr, "cannot write key: %v\n", err) os.Exit(1) } }
// loadConfig initializes and parses the config using command line options. func loadConfig() (*config, []string, error) { // Default config. cfg := config{ DataDir: defaultDataDir, DbType: defaultDbType, NumCandidates: defaultNumCandidates, } // Parse command line options. parser := flags.NewParser(&cfg, flags.Default) remainingArgs, err := parser.Parse() if err != nil { if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { parser.WriteHelp(os.Stderr) } return nil, nil, err } // Multiple networks can't be selected simultaneously. funcName := "loadConfig" numNets := 0 // Count number of network flags passed; assign active network params // while we're at it if cfg.TestNet3 { numNets++ activeNetParams = &chaincfg.TestNet3Params } if cfg.RegressionTest { numNets++ activeNetParams = &chaincfg.RegressionNetParams } if cfg.SimNet { numNets++ activeNetParams = &chaincfg.SimNetParams } if numNets > 1 { str := "%s: The testnet, regtest, and simnet params can't be " + "used together -- choose one of the three" err := fmt.Errorf(str, funcName) fmt.Fprintln(os.Stderr, err) parser.WriteHelp(os.Stderr) return nil, nil, err } // Validate database type. if !validDbType(cfg.DbType) { str := "%s: The specified database type [%v] is invalid -- " + "supported types %v" err := fmt.Errorf(str, "loadConfig", cfg.DbType, knownDbTypes) fmt.Fprintln(os.Stderr, err) parser.WriteHelp(os.Stderr) return nil, nil, err } // Append the network type to the data directory so it is "namespaced" // per network. In addition to the block database, there are other // pieces of data that are saved to disk such as address manager state. // All data is specific to a network, so namespacing the data directory // means each individual piece of serialized data does not have to // worry about changing names per network and such. cfg.DataDir = filepath.Join(cfg.DataDir, netName(activeNetParams)) // Validate the number of candidates. if cfg.NumCandidates < minCandidates || cfg.NumCandidates > maxCandidates { str := "%s: The specified number of candidates is out of " + "range -- parsed [%v]" err = fmt.Errorf(str, "loadConfig", cfg.NumCandidates) fmt.Fprintln(os.Stderr, err) parser.WriteHelp(os.Stderr) return nil, nil, err } return &cfg, remainingArgs, nil }
// loadConfig initializes and parses the config using a config file and command // line options. // // The configuration proceeds as follows: // 1) Start with a default config with sane settings // 2) Pre-parse the command line to check for an alternative config file // 3) Load configuration file overwriting defaults with any specified options // 4) Parse CLI options and overwrite/add any specified options // // The above results in btcwallet functioning properly without any config // settings while still allowing the user to override settings with config files // and command line options. Command line options always take precedence. func loadConfig() (*config, []string, error) { // Default config. cfg := config{ DebugLevel: defaultLogLevel, ConfigFile: defaultConfigFile, DataDir: defaultDataDir, LogDir: defaultLogDir, WalletPass: defaultPubPassphrase, RPCKey: defaultRPCKeyFile, RPCCert: defaultRPCCertFile, DisallowFree: defaultDisallowFree, RPCMaxClients: defaultRPCMaxClients, RPCMaxWebsockets: defaultRPCMaxWebsockets, } // A config file in the current directory takes precedence. if fileExists(defaultConfigFilename) { cfg.ConfigFile = defaultConfigFile } // Pre-parse the command line options to see if an alternative config // file or the version flag was specified. preCfg := cfg preParser := flags.NewParser(&preCfg, flags.Default) _, err := preParser.Parse() if err != nil { if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { preParser.WriteHelp(os.Stderr) } return nil, nil, err } // Show the version and exit if the version flag was specified. funcName := "loadConfig" appName := filepath.Base(os.Args[0]) appName = strings.TrimSuffix(appName, filepath.Ext(appName)) usageMessage := fmt.Sprintf("Use %s -h to show usage", appName) if preCfg.ShowVersion { fmt.Println(appName, "version", version()) os.Exit(0) } // Load additional config from file. var configFileError error parser := flags.NewParser(&cfg, flags.Default) err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) if err != nil { if _, ok := err.(*os.PathError); !ok { fmt.Fprintln(os.Stderr, err) parser.WriteHelp(os.Stderr) return nil, nil, err } configFileError = err } // Parse command line options again to ensure they take precedence. remainingArgs, err := parser.Parse() if err != nil { if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { parser.WriteHelp(os.Stderr) } return nil, nil, err } // Warn about missing config file after the final command line parse // succeeds. This prevents the warning on help messages and invalid // options. if configFileError != nil { log.Warnf("%v", configFileError) } // If an alternate data directory was specified, and paths with defaults // relative to the data dir are unchanged, modify each path to be // relative to the new data dir. if cfg.DataDir != defaultDataDir { if cfg.RPCKey == defaultRPCKeyFile { cfg.RPCKey = filepath.Join(cfg.DataDir, "rpc.key") } if cfg.RPCCert == defaultRPCCertFile { cfg.RPCCert = filepath.Join(cfg.DataDir, "rpc.cert") } } // Choose the active network params based on the selected network. // Multiple networks can't be selected simultaneously. numNets := 0 if cfg.MainNet { activeNet = &mainNetParams numNets++ } if cfg.SimNet { activeNet = &simNetParams numNets++ } if numNets > 1 { str := "%s: The mainnet and simnet params can't be used " + "together -- choose one" err := fmt.Errorf(str, "loadConfig") fmt.Fprintln(os.Stderr, err) parser.WriteHelp(os.Stderr) return nil, nil, err } // Append the network type to the log directory so it is "namespaced" // per network. cfg.LogDir = cleanAndExpandPath(cfg.LogDir) cfg.LogDir = filepath.Join(cfg.LogDir, activeNet.Params.Name) // Special show command to list supported subsystems and exit. if cfg.DebugLevel == "show" { fmt.Println("Supported subsystems", supportedSubsystems()) os.Exit(0) } // Initialize logging at the default logging level. initSeelogLogger(filepath.Join(cfg.LogDir, defaultLogFilename)) setLogLevels(defaultLogLevel) // Parse, validate, and set debug log level(s). if err := parseAndSetDebugLevels(cfg.DebugLevel); err != nil { err := fmt.Errorf("%s: %v", "loadConfig", err.Error()) fmt.Fprintln(os.Stderr, err) parser.WriteHelp(os.Stderr) return nil, nil, err } // Exit if you try to use a simulation wallet with a standard // data directory. if cfg.DataDir == defaultDataDir && cfg.CreateTemp { fmt.Fprintln(os.Stderr, "Tried to create a temporary simulation "+ "wallet, but failed to specify data directory!") os.Exit(0) } // Exit if you try to use a simulation wallet on anything other than // simnet or testnet3. if !cfg.SimNet && cfg.CreateTemp { fmt.Fprintln(os.Stderr, "Tried to create a temporary simulation "+ "wallet for network other than simnet!") os.Exit(0) } // Ensure the wallet exists or create it when the create flag is set. netDir := networkDir(cfg.DataDir, activeNet.Params) dbPath := filepath.Join(netDir, walletDbName) if cfg.CreateTemp && cfg.Create { err := fmt.Errorf("The flags --create and --createtemp can not " + "be specified together. Use --help for more information.") fmt.Fprintln(os.Stderr, err) return nil, nil, err } if cfg.CreateTemp { tempWalletExists := false if fileExists(dbPath) { str := fmt.Sprintf("The wallet already exists. Loading this " + "wallet instead.") fmt.Fprintln(os.Stdout, str) tempWalletExists = true } // Ensure the data directory for the network exists. if err := checkCreateDir(netDir); err != nil { fmt.Fprintln(os.Stderr, err) return nil, nil, err } if !tempWalletExists { // Perform the initial wallet creation wizard. if err := createSimulationWallet(&cfg); err != nil { fmt.Fprintln(os.Stderr, "Unable to create wallet:", err) return nil, nil, err } } } else if cfg.Create { // Error if the create flag is set and the wallet already // exists. if fileExists(dbPath) { err := fmt.Errorf("The wallet already exists.") fmt.Fprintln(os.Stderr, err) return nil, nil, err } // Ensure the data directory for the network exists. if err := checkCreateDir(netDir); err != nil { fmt.Fprintln(os.Stderr, err) return nil, nil, err } // Perform the initial wallet creation wizard. if err := createWallet(&cfg); err != nil { fmt.Fprintln(os.Stderr, "Unable to create wallet:", err) return nil, nil, err } // Created successfully, so exit now with success. os.Exit(0) } else if !fileExists(dbPath) { var err error keystorePath := filepath.Join(netDir, keystore.Filename) if !fileExists(keystorePath) { err = fmt.Errorf("The wallet does not exist. Run with the " + "--create option to initialize and create it.") } else { err = fmt.Errorf("The wallet is in legacy format. Run with the " + "--create option to import it.") } fmt.Fprintln(os.Stderr, err) return nil, nil, err } if cfg.RPCConnect == "" { cfg.RPCConnect = activeNet.connect } // Add default port to connect flag if missing. cfg.RPCConnect = normalizeAddress(cfg.RPCConnect, activeNet.btcdPort) localhostListeners := map[string]struct{}{ "localhost": struct{}{}, "127.0.0.1": struct{}{}, "::1": struct{}{}, } RPCHost, _, err := net.SplitHostPort(cfg.RPCConnect) if err != nil { return nil, nil, err } if cfg.DisableClientTLS { if _, ok := localhostListeners[RPCHost]; !ok { str := "%s: the --noclienttls option may not be used " + "when connecting RPC to non localhost " + "addresses: %s" err := fmt.Errorf(str, funcName, cfg.RPCConnect) fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, usageMessage) return nil, nil, err } } else { // If CAFile is unset, choose either the copy or local btcd cert. if cfg.CAFile == "" { cfg.CAFile = filepath.Join(cfg.DataDir, defaultCAFilename) // If the CA copy does not exist, check if we're connecting to // a local btcd and switch to its RPC cert if it exists. if !fileExists(cfg.CAFile) { if _, ok := localhostListeners[RPCHost]; ok { if fileExists(btcdHomedirCAFile) { cfg.CAFile = btcdHomedirCAFile } } } } } if len(cfg.SvrListeners) == 0 { addrs, err := net.LookupHost("localhost") if err != nil { return nil, nil, err } cfg.SvrListeners = make([]string, 0, len(addrs)) for _, addr := range addrs { addr = net.JoinHostPort(addr, activeNet.svrPort) cfg.SvrListeners = append(cfg.SvrListeners, addr) } } // Add default port to all rpc listener addresses if needed and remove // duplicate addresses. cfg.SvrListeners = normalizeAddresses(cfg.SvrListeners, activeNet.svrPort) // Only allow server TLS to be disabled if the RPC is bound to localhost // addresses. if cfg.DisableServerTLS { for _, addr := range cfg.SvrListeners { host, _, err := net.SplitHostPort(addr) if err != nil { str := "%s: RPC listen interface '%s' is " + "invalid: %v" err := fmt.Errorf(str, funcName, addr, err) fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, usageMessage) return nil, nil, err } if _, ok := localhostListeners[host]; !ok { str := "%s: the --noservertls option may not be used " + "when binding RPC to non localhost " + "addresses: %s" err := fmt.Errorf(str, funcName, addr) fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, usageMessage) return nil, nil, err } } } // Expand environment variable and leading ~ for filepaths. cfg.CAFile = cleanAndExpandPath(cfg.CAFile) // If the btcd username or password are unset, use the same auth as for // the client. The two settings were previously shared for btcd and // client auth, so this avoids breaking backwards compatibility while // allowing users to use different auth settings for btcd and wallet. if cfg.BtcdUsername == "" { cfg.BtcdUsername = cfg.Username } if cfg.BtcdPassword == "" { cfg.BtcdPassword = cfg.Password } return &cfg, remainingArgs, nil }
// loadConfig initializes and parses the config using a config file and command // line options. // // The configuration proceeds as follows: // 1) Start with a default config with sane settings // 2) Pre-parse the command line to check for an alternative config file // 3) Load configuration file overwriting defaults with any specified options // 4) Parse CLI options and overwrite/add any specified options // // The above results in functioning properly without any config settings // while still allowing the user to override settings with config files and // command line options. Command line options always take precedence. func loadConfig() (*config, []string, error) { // Default config. cfg := config{ ConfigFile: defaultConfigFile, RPCServer: defaultRPCServer, RPCCert: defaultRPCCertFile, AccessTokenFile: defaultAccessToken, RelayUrl: defaultRelayUrl, } // Create the home directory if it doesn't already exist. err := os.MkdirAll(retweeterHomeDir, 0700) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(-1) } // Pre-parse the command line options to see if an alternative config // file, the version flag, or the list commands flag was specified. Any // errors aside from the help message error can be ignored here since // they will be caught by the final parse below. preCfg := cfg preParser := flags.NewParser(&preCfg, flags.HelpFlag) _, err = preParser.Parse() if err != nil { if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { return nil, nil, err } } // Show the version and exit if the version flag was specified. appName := filepath.Base(os.Args[0]) appName = strings.TrimSuffix(appName, filepath.Ext(appName)) usageMessage := fmt.Sprintf("Use %s -h to show options", appName) // Load additional config from file. parser := flags.NewParser(&cfg, flags.Default) err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) if err != nil { if _, ok := err.(*os.PathError); !ok { fmt.Fprintf(os.Stderr, "Error parsing config file: %v\n", err) fmt.Fprintln(os.Stderr, usageMessage) return nil, nil, err } } // Parse command line options again to ensure they take precedence. remainingArgs, err := parser.Parse() if err != nil { if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { fmt.Fprintln(os.Stderr, usageMessage) } return nil, nil, err } // Set activeNet for the application activeNet = chaincfg.MainNetParams if cfg.TestNet3 { activeNet = chaincfg.TestNet3Params } // Handle environment variable expansion in the RPC certificate path. cfg.RPCCert = cleanAndExpandPath(cfg.RPCCert) // Add default port to RPC server based on --testnet and --wallet flags // if needed. cfg.RPCServer = normalizeAddress(cfg.RPCServer, cfg.TestNet3, false, true) hasField("hashtag", cfg.Hashtag) //hasField("sending address", cfg.SendAddress) hasField("consumer key", cfg.ConsumerKey) hasField("consumer secret", cfg.ConsumerSecret) hasField("wallet passphrase", cfg.WalletPassphrase) return &cfg, remainingArgs, nil }
// loadConfig initializes and parses the config using a config file and command // line options. // // The configuration proceeds as follows: // 1) Start with a default config with sane settings // 2) Pre-parse the command line to check for an alternative config file // 3) Load configuration file overwriting defaults with any specified options // 4) Parse CLI options and overwrite/add any specified options // // The above results in functioning properly without any config settings // while still allowing the user to override settings with config files and // command line options. Command line options always take precedence. func loadConfig() (*config, []string, error) { // Default config. cfg := config{ ConfigFile: defaultConfigFile, RPCServer: defaultRPCServer, RPCCert: defaultRPCCertFile, } // Create the home directory if it doesn't already exist. err := os.MkdirAll(dcrdHomeDir, 0700) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(-1) } // Pre-parse the command line options to see if an alternative config // file, the version flag, or the list commands flag was specified. Any // errors aside from the help message error can be ignored here since // they will be caught by the final parse below. preCfg := cfg preParser := flags.NewParser(&preCfg, flags.HelpFlag) _, err = preParser.Parse() if err != nil { if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, "") fmt.Fprintln(os.Stderr, "The special parameter `-` "+ "indicates that a parameter should be read "+ "from the\nnext unread line from standard "+ "input.") return nil, nil, err } } // Load additional config from file. appName := filepath.Base(os.Args[0]) appName = strings.TrimSuffix(appName, filepath.Ext(appName)) usageMessage := fmt.Sprintf("Use %s -h to show options", appName) parser := flags.NewParser(&cfg, flags.Default) err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) if err != nil { if _, ok := err.(*os.PathError); !ok { fmt.Fprintf(os.Stderr, "Error parsing config file: %v\n", err) fmt.Fprintln(os.Stderr, usageMessage) return nil, nil, err } } // Parse command line options again to ensure they take precedence. remainingArgs, err := parser.Parse() if err != nil { if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { fmt.Fprintln(os.Stderr, usageMessage) } return nil, nil, err } // Handle environment variable expansion in the RPC certificate path. cfg.RPCCert = cleanAndExpandPath(cfg.RPCCert) // Add default port to RPC server if needed. cfg.RPCServer = normalizeAddress(cfg.RPCServer) return &cfg, remainingArgs, nil }
func main() { cfg := config{ DbType: "leveldb", DataDir: defaultDataDir, } parser := flags.NewParser(&cfg, flags.Default) _, err := parser.Parse() if err != nil { if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { parser.WriteHelp(os.Stderr) } return } backendLogger := btclog.NewDefaultBackendLogger() defer backendLogger.Flush() log = btclog.NewSubsystemLogger(backendLogger, "") database.UseLogger(log) // Multiple networks can't be selected simultaneously. funcName := "main" numNets := 0 // Count number of network flags passed; assign active network params // while we're at it if cfg.TestNet3 { numNets++ activeNetParams = &chaincfg.TestNet3Params } if cfg.RegressionTest { numNets++ activeNetParams = &chaincfg.RegressionNetParams } if cfg.SimNet { numNets++ activeNetParams = &chaincfg.SimNetParams } if numNets > 1 { str := "%s: The testnet, regtest, and simnet params can't be " + "used together -- choose one of the three" err := fmt.Errorf(str, funcName) fmt.Fprintln(os.Stderr, err) parser.WriteHelp(os.Stderr) return } cfg.DataDir = filepath.Join(cfg.DataDir, netName(activeNetParams)) blockDbNamePrefix := "blocks" dbName := blockDbNamePrefix + "_" + cfg.DbType if cfg.DbType == "sqlite" { dbName = dbName + ".db" } dbPath := filepath.Join(cfg.DataDir, dbName) log.Infof("loading db") db, err := database.OpenDB(cfg.DbType, dbPath) if err != nil { log.Warnf("db open failed: %v", err) return } defer db.Close() log.Infof("db load complete") _, height, err := db.NewestSha() log.Infof("loaded block height %v", height) sha, err := getSha(db, cfg.ShaString) if err != nil { log.Infof("Invalid block hash %v", cfg.ShaString) return } err = db.DropAfterBlockBySha(&sha) if err != nil { log.Warnf("failed %v", err) } }
func main() { end := int64(-1) cfg := config{ DbType: "leveldb", DataDir: defaultDataDir, } parser := flags.NewParser(&cfg, flags.Default) _, err := parser.Parse() if err != nil { if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { parser.WriteHelp(os.Stderr) } return } backendLogger := btclog.NewDefaultBackendLogger() defer backendLogger.Flush() log = btclog.NewSubsystemLogger(backendLogger, "") database.UseLogger(log) // Multiple networks can't be selected simultaneously. funcName := "main" numNets := 0 // Count number of network flags passed; assign active network params // while we're at it if cfg.TestNet { numNets++ activeNetParams = &chaincfg.TestNetParams } if cfg.SimNet { numNets++ activeNetParams = &chaincfg.SimNetParams } if numNets > 1 { str := "%s: The testnet, regtest, and simnet params can't be " + "used together -- choose one of the three" err := fmt.Errorf(str, funcName) fmt.Fprintln(os.Stderr, err) parser.WriteHelp(os.Stderr) return } cfg.DataDir = filepath.Join(cfg.DataDir, netName(activeNetParams)) blockDbNamePrefix := "blocks" dbName := blockDbNamePrefix + "_" + cfg.DbType if cfg.DbType == "sqlite" { dbName = dbName + ".db" } dbPath := filepath.Join(cfg.DataDir, dbName) log.Infof("loading db %v", cfg.DbType) database, err := database.OpenDB(cfg.DbType, dbPath) if err != nil { log.Warnf("db open failed: %v", err) return } defer database.Close() log.Infof("db load complete") height, err := getHeight(database, cfg.ShaString) if err != nil { log.Infof("Invalid block %v", cfg.ShaString) return } if cfg.EShaString != "" { end, err = getHeight(database, cfg.EShaString) if err != nil { log.Infof("Invalid end block %v", cfg.EShaString) return } } else { end = height + 1 } log.Infof("height %v end %v", height, end) var fo io.WriteCloser if cfg.OutFile != "" { fo, err = os.Create(cfg.OutFile) if err != nil { log.Warnf("failed to open file %v, err %v", cfg.OutFile, err) } defer func() { if err := fo.Close(); err != nil { log.Warn("failed to close file %v %v", cfg.OutFile, err) } }() } for ; height < end; height++ { if cfg.Progress && height%int64(1) == 0 { log.Infof("Processing block %v", height) } err = DumpBlock(database, height, fo, cfg.RawBlock, cfg.FmtBlock, cfg.ShowTx) if err != nil { break } } if cfg.Progress { height-- log.Infof("Processing block %v", height) } }
// loadConfig initializes and parses the config using a config file and command // line options. // // The configuration proceeds as follows: // 1) Start with a default config with sane settings // 2) Pre-parse the command line to check for an alternative config file // 3) Load configuration file overwriting defaults with any specified options // 4) Parse CLI options and overwrite/add any specified options // // The above results in functioning properly without any config settings // while still allowing the user to override settings with config files and // command line options. Command line options always take precedence. func loadConfig() (*config, []string, error) { // Default config. cfg := config{ ConfigFile: defaultConfigFile, RPCServer: defaultRPCServer, RPCCert: defaultRPCCertFile, } // Create the home directory if it doesn't already exist. err := os.MkdirAll(btcdHomeDir, 0700) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(-1) } // Pre-parse the command line options to see if an alternative config // file, the version flag, or the list commands flag was specified. Any // errors aside from the help message error can be ignored here since // they will be caught by the final parse below. preCfg := cfg preParser := flags.NewParser(&preCfg, flags.HelpFlag) _, err = preParser.Parse() if err != nil { if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, "") fmt.Fprintln(os.Stderr, "The special parameter `-` "+ "indicates that a parameter should be read "+ "from the\nnext unread line from standard "+ "input.") return nil, nil, err } } // Show the version and exit if the version flag was specified. appName := filepath.Base(os.Args[0]) appName = strings.TrimSuffix(appName, filepath.Ext(appName)) usageMessage := fmt.Sprintf("Use %s -h to show options", appName) if preCfg.ShowVersion { fmt.Println(appName, "version", version()) os.Exit(0) } // Show the available commands and exit if the associated flag was // specified. if preCfg.ListCommands { listCommands() os.Exit(0) } // Load additional config from file. parser := flags.NewParser(&cfg, flags.Default) err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) if err != nil { if _, ok := err.(*os.PathError); !ok { fmt.Fprintf(os.Stderr, "Error parsing config file: %v\n", err) fmt.Fprintln(os.Stderr, usageMessage) return nil, nil, err } } // Parse command line options again to ensure they take precedence. remainingArgs, err := parser.Parse() if err != nil { if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { fmt.Fprintln(os.Stderr, usageMessage) } return nil, nil, err } // Multiple networks can't be selected simultaneously. numNets := 0 if cfg.TestNet3 { numNets++ } if cfg.SimNet { numNets++ } if numNets > 1 { str := "%s: The testnet and simnet params can't be used " + "together -- choose one of the two" err := fmt.Errorf(str, "loadConfig") fmt.Fprintln(os.Stderr, err) return nil, nil, err } // Override the RPC certificate if the --wallet flag was specified and // the user did not specify one. if cfg.Wallet && cfg.RPCCert == defaultRPCCertFile { cfg.RPCCert = defaultWalletCertFile } // Handle environment variable expansion in the RPC certificate path. cfg.RPCCert = cleanAndExpandPath(cfg.RPCCert) // Add default port to RPC server based on --testnet and --wallet flags // if needed. cfg.RPCServer = normalizeAddress(cfg.RPCServer, cfg.TestNet3, cfg.SimNet, cfg.Wallet) return &cfg, remainingArgs, nil }