decred.org/dcrdex@v1.0.3/client/cmd/bwctl/config.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package main
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"path/filepath"
    10  	"runtime"
    11  
    12  	flags "github.com/jessevdk/go-flags"
    13  
    14  	"decred.org/dcrdex/client/rpcserver"
    15  	"decred.org/dcrdex/dex"
    16  	"github.com/decred/dcrd/dcrutil/v4"
    17  )
    18  
    19  const (
    20  	defaultRPCPort        = "5757"
    21  	defaultMainnetHost    = "127.0.0.1"
    22  	defaultTestnetHost    = "127.0.0.2"
    23  	defaultSimnetHost     = "127.0.0.3"
    24  	defaultConfigFilename = "dexcctl.conf"
    25  	defaultRPCCertFile    = "rpc.cert"
    26  )
    27  
    28  var (
    29  	appDir            = dcrutil.AppDataDir("dexcctl", false)
    30  	bisonwAppDir      = dcrutil.AppDataDir("dexc", false)
    31  	defaultConfigPath = filepath.Join(appDir, defaultConfigFilename)
    32  )
    33  
    34  // config defines the configuration options for bwctl.
    35  type config struct {
    36  	ShowVersion  bool     `short:"V" long:"version" description:"Display version information and exit"`
    37  	ListCommands bool     `short:"l" long:"listcommands" description:"List all of the supported commands and exit"`
    38  	Config       string   `short:"C" long:"config" description:"Path to configuration file"`
    39  	RPCUser      string   `short:"u" long:"rpcuser" description:"RPC username"`
    40  	RPCPass      string   `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password"`
    41  	RPCAddr      string   `short:"a" long:"rpcaddr" description:"RPC server to connect to"`
    42  	RPCCert      string   `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"`
    43  	PrintJSON    bool     `short:"j" long:"json" description:"Print json messages sent and received"`
    44  	Proxy        string   `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"`
    45  	ProxyUser    string   `long:"proxyuser" description:"Username for proxy server"`
    46  	ProxyPass    string   `long:"proxypass" default-mask:"-" description:"Password for proxy server"`
    47  	PasswordArgs []string `short:"p" long:"passarg" description:"Password arguments to bypass stdin prompts."`
    48  	Testnet      bool     `long:"testnet" description:"use testnet"`
    49  	Simnet       bool     `long:"simnet" description:"use simnet"`
    50  }
    51  
    52  // configure parses command line options and a config file if present. Returns
    53  // an instantiated *config, leftover command line arguments, and a bool that
    54  // is true if there is nothing further to do (i.e. version was printed and we
    55  // can exit), or a parsing error, in that order.
    56  func configure() (*config, []string, bool, error) {
    57  	stop := true
    58  	cfg := &config{
    59  		Config: defaultConfigPath,
    60  	}
    61  	preParser := flags.NewParser(cfg, flags.HelpFlag|flags.PassDoubleDash|flags.PassAfterNonOption)
    62  	_, err := preParser.Parse()
    63  	if err != nil {
    64  		var flagErr *flags.Error
    65  		if errors.As(err, &flagErr) && flagErr.Type == flags.ErrHelp {
    66  			// This line is printed below the help message.
    67  			fmt.Printf("%v\nThe special parameter `-` indicates that a parameter should be read from the\nnext unread line from standard input.\n", err)
    68  			return nil, nil, stop, nil
    69  		}
    70  		return nil, nil, false, err
    71  	}
    72  
    73  	// Show the version and exit if the version flag was specified.
    74  	if cfg.ShowVersion {
    75  		fmt.Printf("%s version %s (Go version %s %s/%s)\n", appName,
    76  			Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
    77  		fmt.Printf("%s required RPC server version: %s\n", appName, requiredRPCServerVersion.String())
    78  		return nil, nil, stop, nil
    79  	}
    80  
    81  	// Show the available commands and exit if the associated flag was
    82  	// specified.
    83  	if cfg.ListCommands {
    84  		fmt.Println(rpcserver.ListCommands(false))
    85  		return nil, nil, stop, nil
    86  	}
    87  
    88  	parser := flags.NewParser(cfg, flags.Default|flags.PassAfterNonOption)
    89  
    90  	if dex.FileExists(cfg.Config) {
    91  		// Load additional config from file.
    92  		err = flags.NewIniParser(parser).ParseFile(cfg.Config)
    93  		if err != nil {
    94  			return nil, nil, false, err
    95  		}
    96  	}
    97  
    98  	// Parse command line options again to ensure they take precedence.
    99  	remainingArgs, err := parser.Parse()
   100  	if err != nil {
   101  		return nil, nil, false, err
   102  	}
   103  
   104  	if cfg.RPCCert == "" {
   105  		// Check in ~/.dexcctl first.
   106  		cfg.RPCCert = dex.CleanAndExpandPath(filepath.Join(appDir, defaultRPCCertFile))
   107  		if !dex.FileExists(cfg.RPCCert) { // Then in ~/.dexc
   108  			cfg.RPCCert = dex.CleanAndExpandPath(filepath.Join(bisonwAppDir, defaultRPCCertFile))
   109  		}
   110  	} else {
   111  		// Handle environment variable and tilde expansion in the given path.
   112  		cfg.RPCCert = dex.CleanAndExpandPath(cfg.RPCCert)
   113  	}
   114  
   115  	if cfg.Simnet && cfg.Testnet {
   116  		return nil, nil, false, fmt.Errorf("simnet and testnet cannot both be specified")
   117  	}
   118  
   119  	if cfg.RPCAddr == "" {
   120  		var rpcHost string
   121  		switch {
   122  		case cfg.Testnet:
   123  			rpcHost = defaultTestnetHost
   124  		case cfg.Simnet:
   125  			rpcHost = defaultSimnetHost
   126  		default:
   127  			rpcHost = defaultMainnetHost
   128  		}
   129  		cfg.RPCAddr = rpcHost + ":" + defaultRPCPort
   130  	}
   131  
   132  	return cfg, remainingArgs, false, nil
   133  }