lbcwallet/config.go

200 lines
6.4 KiB
Go
Raw Normal View History

2013-08-22 18:30:38 +02:00
/*
* Copyright (c) 2013 Conformal Systems LLC <info@conformal.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
2013-08-21 16:37:30 +02:00
package main
import (
"fmt"
"github.com/conformal/btcwire"
2013-08-21 16:37:30 +02:00
"github.com/conformal/go-flags"
"os"
"path/filepath"
"strings"
)
const (
defaultConfigFilename = "btcwallet.conf"
defaultBtcNet = btcwire.TestNet3
2013-08-21 16:37:30 +02:00
defaultLogLevel = "info"
)
var (
defaultConfigFile = filepath.Join(btcwalletHomeDir(), defaultConfigFilename)
defaultDataDir = btcwalletHomeDir()
2013-08-21 16:37:30 +02:00
)
type config struct {
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
BtcdPort string `short:"b" long:"btcdport" description:"Port to connect to btcd on (default: 18334, mainnet: 18332)"`
2013-08-21 16:37:30 +02:00
DebugLevel string `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"`
ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"`
SvrPort string `short:"p" long:"serverport" description:"Port to serve frontend websocket connections on (default: 18332, mainnet: 8332)"`
DataDir string `short:"D" long:"datadir" description:"Directory to store wallets and transactions"`
Username string `short:"u" long:"username" description:"Username for btcd authorization"`
Password string `short:"P" long:"password" description:"Password for btcd authorization"`
MainNet bool `long:"mainnet" description:"*DISABLED* Use the main Bitcoin network (default testnet3)"`
2013-08-21 16:37:30 +02:00
}
// btcwalletHomeDir returns an OS appropriate home directory for btcwallet.
func btcwalletHomeDir() string {
// Search for Windows APPDATA first. This won't exist on POSIX OSes.
appData := os.Getenv("APPDATA")
if appData != "" {
return filepath.Join(appData, "btcwallet")
}
// Fall back to standard HOME directory that works for most POSIX OSes.
home := os.Getenv("HOME")
if home != "" {
return filepath.Join(home, ".btcwallet")
}
// In the worst case, use the current directory.
return "."
}
// updateConfigWithActiveParams update the passed config with parameters
// from the active net params if the relevant options in the passed config
// object are the default so options specified by the user on the command line
// are not overridden.
func updateConfigWithActiveParams(cfg *config) {
if cfg.BtcdPort == netParams(defaultBtcNet).btcdPort {
cfg.BtcdPort = activeNetParams.btcdPort
}
if cfg.SvrPort == netParams(defaultBtcNet).svrPort {
cfg.SvrPort = activeNetParams.svrPort
}
}
2013-08-21 16:37:30 +02:00
// filesExists reports whether the named file or directory exists.
func fileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// 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,
BtcdPort: netParams(defaultBtcNet).btcdPort,
SvrPort: netParams(defaultBtcNet).svrPort,
DataDir: defaultDataDir,
2013-08-21 16:37:30 +02:00
}
// 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.
if preCfg.ShowVersion {
appName := filepath.Base(os.Args[0])
appName = strings.TrimSuffix(appName, filepath.Ext(appName))
fmt.Println(appName, "version", version())
os.Exit(0)
}
// Load additional config from file.
parser := flags.NewParser(&cfg, flags.Default)
err = parser.ParseIniFile(preCfg.ConfigFile)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return nil, nil, err
}
log.Warnf("%v", 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
}
// TODO(jrick): Enable mainnet support again when ready.
cfg.MainNet = false
// Choose the active network params based on the mainnet net flag.
if cfg.MainNet {
activeNetParams = netParams(btcwire.MainNet)
}
updateConfigWithActiveParams(&cfg)
// Validate debug log level
if !validLogLevel(cfg.DebugLevel) {
str := "%s: The specified debug level [%v] is invalid"
err := fmt.Errorf(str, "loadConfig", cfg.DebugLevel)
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return nil, nil, err
}
2013-08-21 16:37:30 +02:00
return &cfg, remainingArgs, nil
}
// validLogLevel returns whether or not logLevel is a valid debug log level.
func validLogLevel(logLevel string) bool {
switch logLevel {
case "trace":
fallthrough
case "debug":
fallthrough
case "info":
fallthrough
case "warn":
fallthrough
case "error":
fallthrough
case "critical":
return true
}
return false
}