diff --git a/bmd.go b/bmd.go index 7de5dd4..c264674 100644 --- a/bmd.go +++ b/bmd.go @@ -39,7 +39,7 @@ var ( func bmdMain() error { // Load configuration. - tcfg, _, err := loadConfig(false) + tcfg, _, err := loadConfig() if err != nil { return err } diff --git a/config.go b/config.go index a141ffc..c209f9f 100644 --- a/config.go +++ b/config.go @@ -37,7 +37,7 @@ const ( defaultMaxRPCClients = 25 defaultDbType = "boltdb" defaultPort = 8444 // 8444 - defaultRPCPort = 8442 + defaultRPCPort = 8442 defaultMaxUpPerPeer = 2 * 1024 * 1024 // 2MBps defaultMaxDownPerPeer = 2 * 1024 * 1024 // 2MBps defaultMaxOutbound = 10 @@ -345,8 +345,14 @@ func createDir(path, name string) error { } // newConfigParser returns a new command line flags parser. -func newConfigParser(cfg *config, options flags.Options) *flags.Parser { - return flags.NewParser(cfg, options) +func newConfigParser(cfg *config, appName string, options flags.Options) *flags.Parser { + p := flags.NewNamedParser(appName, options) + + if cfg != nil { + p.AddGroup("Application Options", "", cfg) + } + + return p } // loadConfig initializes and parses the config using a config file and command @@ -362,7 +368,15 @@ func newConfigParser(cfg *config, options flags.Options) *flags.Parser { // The above results in bmd 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(isTest bool) (*config, []string, error) { +func loadConfig() (*config, []string, error) { + + appName := filepath.Base(os.Args[0]) + appName = strings.TrimSuffix(appName, filepath.Ext(appName)) + + return LoadConfig(appName, os.Args[1:]) +} + +func LoadConfig(appName string, args []string) (*config, []string, error) { // Default config. cfg := config{ ConfigFile: defaultConfigFile, @@ -389,20 +403,16 @@ func loadConfig(isTest bool) (*config, []string, error) { // the final parse below. preCfg := cfg var err error - if !isTest { - preParser := newConfigParser(&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) - return nil, nil, err - } + preParser := newConfigParser(&preCfg, appName, flags.HelpFlag) + _, err = preParser.ParseArgs(args) + if err != nil { + if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { + fmt.Fprintln(os.Stderr, err) + 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 usage", appName) if preCfg.ShowVersion { fmt.Println(appName, "version", version()) @@ -411,10 +421,12 @@ func loadConfig(isTest bool) (*config, []string, error) { // Load additional config from file. var configFileError error - parser := newConfigParser(&cfg, flags.Default) - if preCfg.ConfigFile != defaultConfigFile { - + parser := newConfigParser(&cfg, appName, flags.Default) + // If the default location is specified, then the file isn't required to exist. + if preCfg.ConfigFile != defaultConfigFile || fileExists(preCfg.ConfigFile) { + err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) + if err != nil { if _, ok := err.(*os.PathError); !ok { fmt.Fprintf(os.Stderr, "Error parsing config "+ @@ -426,16 +438,14 @@ func loadConfig(isTest bool) (*config, []string, error) { } } + // Parse command line options again to ensure they take precedence. var remainingArgs []string - if !isTest { - // 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 + remainingArgs, err = parser.ParseArgs(args) + if err != nil { + if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { + fmt.Fprintln(os.Stderr, usageMessage) } + return nil, nil, err } funcName := "loadConfig" diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000..7768ea9 --- /dev/null +++ b/config_test.go @@ -0,0 +1,174 @@ +// Copyright 2016 Daniel Krawisz. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package main + +import ( + "testing" + "io/ioutil" + "os" + "strconv" + "fmt" +) + +// If config files exist while we are doing +var oldDefaultConfigFile []byte = nil +var oldConfigFile []byte = nil +var oldConfigFilename *string = nil + +func setup(defaultConfigContents, configFileContents, configFilename *string) error { + var err error + + // Check if a default config file exists. If so, save it and remove it. + if _, err = os.Stat(defaultConfigFile); !os.IsNotExist(err) { + oldDefaultConfigFile, err = ioutil.ReadFile(defaultConfigFile) + + if err != nil { + return err + } + + err = os.Remove(defaultConfigFile) + if err != nil { + oldDefaultConfigFile = nil + return err + } + } + + // Check if defaultConfigContents is set. If so, make a config file. + if defaultConfigContents != nil { + err = ioutil.WriteFile(defaultConfigFile, []byte(*defaultConfigContents), 0644) + if err != nil { + cleanup() + return nil + } + } + + // Check if configFilePath is set and is not equal to the default + // path. + if configFilename == nil || *configFilename == defaultConfigFile { + return nil + } else { + oldConfigFilename = configFilename + } + + // If the file exists, save it. + if _, err = os.Stat(*configFilename); !os.IsNotExist(err) { + oldConfigFile, err = ioutil.ReadFile(*configFilename) + + if err != nil { + return err + } + + err = os.Remove(*configFilename) + if err != nil { + oldConfigFile = nil + return err + } + } + + if configFileContents != nil { + err = ioutil.WriteFile(*configFilename, []byte(*configFileContents), 0644) + if err != nil { + cleanup() + return nil + } + } + + return nil +} + +func cleanup() { + if oldConfigFile == nil { + if _, err := os.Stat(defaultConfigFile); !os.IsNotExist(err) { + os.Remove(defaultConfigFile) + } + } else { + ioutil.WriteFile(defaultConfigFile, oldDefaultConfigFile, 0644) + } + + if oldConfigFilename != nil { + if oldConfigFile == nil { + os.Remove(*oldConfigFilename) + } else { + ioutil.WriteFile(*oldConfigFilename, oldDefaultConfigFile, 0644) + } + } + + oldConfigFile = nil; + oldConfigFilename = nil; + oldDefaultConfigFile = nil; +} + +func testConfig(t *testing.T, testId int, expected uint64, cmdLine *uint64, defaultConfig *uint64, config *uint64, configFile *string) { + var defaultConfigContents *string + var configFileContents *string + var commandLine []string = make([]string, 0) + + defer cleanup() + + // first construct the command-line arguments. + if cmdLine != nil { + commandLine = append(commandLine, fmt.Sprintf("--maxpeers=%s", strconv.FormatUint(*cmdLine, 10))) + } + if configFile != nil { + commandLine = append(commandLine, fmt.Sprintf("--configfile=%s", *configFile)) + } + + // Make the default config file. + if defaultConfig != nil { + var dcc string = fmt.Sprintf("maxpeers=%s", strconv.FormatUint(*defaultConfig, 10)) + defaultConfigContents = &dcc + } + + // Make the extra config file. + if config != nil { + var cc string = fmt.Sprintf("maxpeers=%s", strconv.FormatUint(*config, 10)) + configFileContents = &cc + } + + // Set up the test. + err := setup(defaultConfigContents, configFileContents, configFile) + if err != nil { + t.Fail() + } + + cfg, _, err := LoadConfig("test", commandLine) + + if cfg == nil { + t.Errorf("Error, test id %d: nil config returned! %s", testId, err.Error()) + return + } + + if cfg.MaxPeers != int(expected) { + t.Errorf("Error, test id %d: expected %d got %d.", testId, expected, cfg.MaxPeers) + } + +} + +func TestLoadConfig(t *testing.T) { + + // Test that an option is correctly set by default when + // no such option is specified in the default config file + // or on the command line. + testConfig(t, 1, defaultMaxPeers, nil, nil, nil, nil) + + // Test that an option is correctly set when specified + // on the command line. + var q uint64 = 97 + testConfig(t, 2, q, &q, nil, nil, nil) + + // Test that an option is correctly set when specified + // in the default config file without a command line + // option set. + var cfg string = "altbmd.conf" + testConfig(t, 3, q, nil, &q, nil, nil) + testConfig(t, 4, q, nil, nil, &q, &cfg) + + // Test that an option is correctly set when specified + // on the command line and that it overwrites the + // option in the config file. + var z uint64 = 39 + testConfig(t, 5, q, &q, &z, nil, nil) + testConfig(t, 6, q, &q, nil, &z, &cfg) +} \ No newline at end of file diff --git a/peer_test.go b/peer_test.go index f95ea17..9b9ec37 100644 --- a/peer_test.go +++ b/peer_test.go @@ -146,7 +146,7 @@ func init() { // Load config var err error - cfg, _, err = loadConfig(true) + cfg, _, err = loadConfig() if err != nil { panic(fmt.Sprint("Config failed to load: ", err)) }