From c2bec24f515dbf66c5861b85586baaa0437d573d Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Tue, 19 Nov 2013 14:38:58 -0600 Subject: [PATCH] Improve btcctl config. This commit improves the configuration for btcctl in several ways: - Add the ability to specify a config file - Add a default entry to the rpc cert to point to the location it will likely be in the btcd home directory - Move the config bits into a separate file for easier maintenance --- util/btcctl/btcctl.go | 17 ++-------- util/btcctl/config.go | 74 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 15 deletions(-) create mode 100644 util/btcctl/config.go diff --git a/util/btcctl/btcctl.go b/util/btcctl/btcctl.go index 399abb415..2cbb0b1b4 100644 --- a/util/btcctl/btcctl.go +++ b/util/btcctl/btcctl.go @@ -13,14 +13,6 @@ import ( "strconv" ) -type config struct { - RpcUser string `short:"u" long:"rpcuser" description:"RPC username"` - RpcPassword string `short:"P" long:"rpcpass" description:"RPC password"` - RpcServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"` - RpcCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"` - TlsSkipVerify bool `long:"skipverify" description:"Do not verify tls certificates (not recommended!)"` -} - // conversionHandler is a handler that is used to convert parameters from the // command line to a specific type. This is needed since the btcjson API // expects certain types for various parameters. @@ -367,12 +359,7 @@ func usage(parser *flags.Parser) { } func main() { - // Parse command line and show usage if needed. - cfg := config{ - RpcServer: "127.0.0.1:8334", - } - parser := flags.NewParser(&cfg, flags.PassDoubleDash|flags.HelpFlag) - args, err := parser.Parse() + parser, cfg, args, err := loadConfig() if err != nil { usage(parser) os.Exit(1) @@ -391,7 +378,7 @@ func main() { } // Execute the command. - err = commandHandler(&cfg, args[0], data, args[1:]) + err = commandHandler(cfg, args[0], data, args[1:]) if err != nil { if err == ErrUsage { usage(parser) diff --git a/util/btcctl/config.go b/util/btcctl/config.go new file mode 100644 index 000000000..a4841f9ea --- /dev/null +++ b/util/btcctl/config.go @@ -0,0 +1,74 @@ +package main + +import ( + "fmt" + "github.com/conformal/btcutil" + "github.com/conformal/go-flags" + "os" + "path/filepath" +) + +var ( + btcdHomeDir = btcutil.AppDataDir("btcd", false) + btcctlHomeDir = btcutil.AppDataDir("btcctl", false) + defaultConfigFile = filepath.Join(btcctlHomeDir, "btcctl.conf") + defaultRPCCertFile = filepath.Join(btcdHomeDir, "rpc.cert") +) + +// config defines the configuration options for btcctl. +// +// See loadConfig for details on the configuration load process. +type config struct { + ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"` + RpcUser string `short:"u" long:"rpcuser" description:"RPC username"` + RpcPassword string `short:"P" long:"rpcpass" description:"RPC password"` + RpcServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"` + RpcCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"` + TlsSkipVerify bool `long:"skipverify" description:"Do not verify tls certificates (not recommended!)"` +} + +// 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() (*flags.Parser, *config, []string, error) { + // Default config. + cfg := config{ + ConfigFile: defaultConfigFile, + RpcServer: "localhost:8334", + RpcCert: defaultRPCCertFile, + } + + // Pre-parse the command line options to see if an alternative config + // file was specified. Any errors can be ignored here since they will + // be caught be the final parse below. + preCfg := cfg + preParser := flags.NewParser(&preCfg, flags.None) + preParser.Parse() + + // Load additional config from file. + parser := flags.NewParser(&cfg, flags.PassDoubleDash|flags.HelpFlag) + err := parser.ParseIniFile(preCfg.ConfigFile) + if err != nil { + if _, ok := err.(*os.PathError); !ok { + fmt.Fprintln(os.Stderr, err) + return parser, nil, nil, err + } + } + + // Parse command line options again to ensure they take precedence. + remainingArgs, err := parser.Parse() + if err != nil { + return parser, nil, nil, err + } + + return parser, &cfg, remainingArgs, nil +}