Add automatic RPC configuration.
This commit is contained in:
parent
6229e35835
commit
ff4ada0b0e
3 changed files with 180 additions and 0 deletions
|
@ -6,9 +6,11 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
"github.com/btcsuite/btcd/btcjson"
|
||||||
|
@ -215,6 +217,13 @@ func loadConfig() (*config, []string, error) {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(preCfg.ConfigFile); os.IsNotExist(err) {
|
||||||
|
err := createDefaultConfigFile(preCfg.ConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error creating a default config file: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load additional config from file.
|
// Load additional config from file.
|
||||||
parser := flags.NewParser(&cfg, flags.Default)
|
parser := flags.NewParser(&cfg, flags.Default)
|
||||||
err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
|
err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
|
||||||
|
@ -268,3 +277,56 @@ func loadConfig() (*config, []string, error) {
|
||||||
|
|
||||||
return &cfg, remainingArgs, nil
|
return &cfg, remainingArgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createDefaultConfig creates a basic config file at the given destination path.
|
||||||
|
// For this it tries to read the btcd config file at its default path, and extract
|
||||||
|
// the RPC user and password from it.
|
||||||
|
func createDefaultConfigFile(destinationPath string) error {
|
||||||
|
// Create the destination directory if it does not exists
|
||||||
|
os.MkdirAll(filepath.Dir(destinationPath), 0700)
|
||||||
|
|
||||||
|
// Read btcd.conf from its default path
|
||||||
|
btcdConfigPath := filepath.Join(btcdHomeDir, "btcd.conf")
|
||||||
|
btcdConfigFile, err := os.Open(btcdConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer btcdConfigFile.Close()
|
||||||
|
content, err := ioutil.ReadAll(btcdConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the rpcuser
|
||||||
|
rpcUserRegexp, err := regexp.Compile(`(?m)^\s*rpcuser=([^\s]+)`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
userSubmatches := rpcUserRegexp.FindSubmatch(content)
|
||||||
|
if userSubmatches == nil {
|
||||||
|
// No user found, nothing to do
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the rpcpass
|
||||||
|
rpcPassRegexp, err := regexp.Compile(`(?m)^\s*rpcpass=([^\s]+)`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
passSubmatches := rpcPassRegexp.FindSubmatch(content)
|
||||||
|
if passSubmatches == nil {
|
||||||
|
// No password found, nothing to do
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the destination file and write the rpcuser and rpcpass to it
|
||||||
|
dest, err := os.OpenFile(destinationPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0700)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dest.Close()
|
||||||
|
|
||||||
|
dest.WriteString(fmt.Sprintf("rpcuser=%s\nrpcpass=%s", string(userSubmatches[1]), string(passSubmatches[1])))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
71
config.go
71
config.go
|
@ -5,8 +5,12 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -395,6 +399,13 @@ func loadConfig() (*config, []string, error) {
|
||||||
if !(preCfg.RegressionTest || preCfg.SimNet) || preCfg.ConfigFile !=
|
if !(preCfg.RegressionTest || preCfg.SimNet) || preCfg.ConfigFile !=
|
||||||
defaultConfigFile {
|
defaultConfigFile {
|
||||||
|
|
||||||
|
if _, err := os.Stat(preCfg.ConfigFile); os.IsNotExist(err) {
|
||||||
|
err := createDefaultConfigFile(preCfg.ConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
btcdLog.Warnf("Error creating a default config file: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err := flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
|
err := flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(*os.PathError); !ok {
|
if _, ok := err.(*os.PathError); !ok {
|
||||||
|
@ -880,6 +891,66 @@ func loadConfig() (*config, []string, error) {
|
||||||
return &cfg, remainingArgs, nil
|
return &cfg, remainingArgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createDefaultConfig copies the file sample-btcd.conf to the given destination path,
|
||||||
|
// and populates it with some randomly generated RPC username and password.
|
||||||
|
func createDefaultConfigFile(destinationPath string) error {
|
||||||
|
// Create the destination directory if it does not exists
|
||||||
|
os.MkdirAll(filepath.Dir(destinationPath), 0700)
|
||||||
|
|
||||||
|
// We get the sample config file path, which is in the same directory as this file.
|
||||||
|
_, path, _, _ := runtime.Caller(0)
|
||||||
|
sampleConfigPath := filepath.Join(filepath.Dir(path), "sample-btcd.conf")
|
||||||
|
|
||||||
|
// We generate a random user and password
|
||||||
|
randomBytes := make([]byte, 20)
|
||||||
|
_, err := rand.Read(randomBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
generatedRPCUser := base64.StdEncoding.EncodeToString(randomBytes)
|
||||||
|
|
||||||
|
_, err = rand.Read(randomBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
generatedRPCPass := base64.StdEncoding.EncodeToString(randomBytes)
|
||||||
|
|
||||||
|
src, err := os.Open(sampleConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
|
||||||
|
dest, err := os.OpenFile(destinationPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0700)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dest.Close()
|
||||||
|
|
||||||
|
// We copy every line from the sample config file to the destination,
|
||||||
|
// only replacing the two lines for rpcuser and rpcpass
|
||||||
|
reader := bufio.NewReader(src)
|
||||||
|
for err != io.EOF {
|
||||||
|
var line string
|
||||||
|
line, err = reader.ReadString('\n')
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(line, "rpcuser=") {
|
||||||
|
line = "rpcuser=" + string(generatedRPCUser) + "\n"
|
||||||
|
} else if strings.Contains(line, "rpcpass=") {
|
||||||
|
line = "rpcpass=" + string(generatedRPCPass) + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := dest.WriteString(line); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// btcdDial connects to the address on the named network using the appropriate
|
// btcdDial connects to the address on the named network using the appropriate
|
||||||
// dial function depending on the address and configuration options. For
|
// dial function depending on the address and configuration options. For
|
||||||
// example, .onion addresses will be dialed using the onion specific proxy if
|
// example, .onion addresses will be dialed using the onion specific proxy if
|
||||||
|
|
47
config_test.go
Normal file
47
config_test.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
rpcuserRegexp = regexp.MustCompile("(?m)^rpcuser=.+$")
|
||||||
|
rpcpassRegexp = regexp.MustCompile("(?m)^rpcpass=.+$")
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateDefaultConfigFile(t *testing.T) {
|
||||||
|
// Setup a temporary directory
|
||||||
|
tmpDir, err := ioutil.TempDir("", "btcd")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed creating a temporary directory: %v", err)
|
||||||
|
}
|
||||||
|
testpath := filepath.Join(tmpDir, "test.conf")
|
||||||
|
// Clean-up
|
||||||
|
defer func() {
|
||||||
|
os.Remove(testpath)
|
||||||
|
os.Remove(tmpDir)
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = createDefaultConfigFile(testpath)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create a default config file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := ioutil.ReadFile(testpath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read generated default config file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !rpcuserRegexp.Match(content) {
|
||||||
|
t.Error("Could not find rpcuser in generated default config file.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !rpcpassRegexp.Match(content) {
|
||||||
|
t.Error("Could not find rpcpass in generated default config file.")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue