diff --git a/cmd/ipfs/config.go b/cmd/ipfs/config.go new file mode 100644 index 00000000000..624be5834b7 --- /dev/null +++ b/cmd/ipfs/config.go @@ -0,0 +1,72 @@ +package main + +import ( + "github.com/jbenet/commander" + config "github.com/jbenet/go-ipfs/config" + u "github.com/jbenet/go-ipfs/util" + "os" + "os/exec" +) + +var cmdIpfsConfig = &commander.Command{ + UsageLine: "config", + Short: "See and Edit ipfs options", + Long: `ipfs config - See or Edit ipfs configuration. + + See specific config's values with: + ipfs config datastore.path + Assign a new value with: + ipfs config datastore.path ~/.go-ipfs/datastore + + Open the config file in your editor(from $EDITOR): + ipfs config edit + `, + Run: configCmd, + Subcommands: []*commander.Command{ + cmdIpfsConfigEdit, + }, +} + +var cmdIpfsConfigEdit = &commander.Command{ + UsageLine: "edit", + Short: "Opens the configuration file in the editor.", + Long: `Looks up environment variable $EDITOR and + attempts to open the config file with it. + `, + Run: configEditCmd, +} + +func configCmd(c *commander.Command, inp []string) error { + if len(inp) == 0 { + // "ipfs config" run without parameters + u.POut(c.Long + "\n") + return nil + } + + if len(inp) == 1 { + // "ipfs config" run without one parameter, so this is a value getter + value, err := config.GetValueInConfigFile(inp[0]) + if err != nil { + u.POut("Failed to get config value: " + err.Error() + "\n") + } else { + u.POut(value + "\n") + } + return nil + } + + // "ipfs config" run without two parameter, so this is a value setter + err := config.SetValueInConfigFile(inp[0], inp[1:]) + if err != nil { + u.POut("Failed to set config value: " + err.Error() + "\n") + } + return nil +} + +func configEditCmd(c *commander.Command, _ []string) error { + if editor := os.Getenv("EDITOR"); editor == "" { + u.POut("ENVIRON variable $EDITOR is not assigned \n") + } else { + exec.Command("sh", "-c", editor+" "+config.DefaultConfigFilePath).Start() + } + return nil +} diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index a4d685eec58..f997fadca30 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -41,6 +41,7 @@ Use "ipfs help " for more information about a command. cmdIpfsCat, cmdIpfsLs, cmdIpfsRefs, + cmdIpfsConfig, cmdIpfsVersion, cmdIpfsCommands, cmdIpfsMount, diff --git a/config/config.go b/config/config.go index 953298c5868..21420221eac 100644 --- a/config/config.go +++ b/config/config.go @@ -1,8 +1,8 @@ package config import ( - "os" u "github.com/jbenet/go-ipfs/util" + "os" ) // Identity tracks the configuration of the local node's identity. @@ -22,7 +22,7 @@ type Config struct { Datastore *Datastore } -var defaultConfigFilePath = "~/.go-ipfs/config" +var DefaultConfigFilePath = "~/.go-ipfs/config" var defaultConfigFile = `{ "identity": {}, "datastore": { @@ -35,7 +35,7 @@ var defaultConfigFile = `{ // LoadConfig reads given file and returns the read config, or error. func LoadConfig(filename string) (*Config, error) { if len(filename) == 0 { - filename = defaultConfigFilePath + filename = DefaultConfigFilePath } // tilde expansion on config file diff --git a/config/serialize.go b/config/serialize.go index a88f50a2f27..c4b1c81611a 100644 --- a/config/serialize.go +++ b/config/serialize.go @@ -2,9 +2,13 @@ package config import ( "encoding/json" + "errors" + "fmt" + u "github.com/jbenet/go-ipfs/util" "io/ioutil" "os" "path" + "strings" ) // WriteFile writes the given buffer `buf` into file named `filename`. @@ -36,3 +40,82 @@ func WriteConfigFile(filename string, cfg *Config) error { return WriteFile(filename, buf) } + +// WriteConfigFile writes the config from `cfg` into `filename`. +func GetValueInConfigFile(key string) (value string, err error) { + // reading config file + attrs := strings.Split(key, ".") + + filename, _ := u.TildeExpansion(DefaultConfigFilePath) + buf, err := ioutil.ReadFile(filename) + if err != nil { + return "", err + } + + // deserializing json + var cfg interface{} + var exists bool + + err = json.Unmarshal(buf, &cfg) + if err != nil { + return "", err + } + + for i := range attrs { + cfgMap, isMap := cfg.(map[string]interface{}) + if !isMap { + return "", errors.New(fmt.Sprintf("%s has no attributes", strings.Join(attrs[:i], "."))) + } + cfg, exists = cfgMap[attrs[i]] + if !exists { + return "", errors.New(fmt.Sprintf("Configuration option key \"%s\" not recognized", strings.Join(attrs[:i+1], "."))) + } + val, is_string := cfg.(string) + if is_string { + return val, nil + } + } + return "", errors.New(fmt.Sprintf("%s is not a string", key)) +} + +// WriteConfigFile writes the config from `cfg` into `filename`. +func SetValueInConfigFile(key string, values []string) error { + assignee := strings.Join(values, " ") + attrs := strings.Split(key, ".") + + filename, _ := u.TildeExpansion(DefaultConfigFilePath) + buf, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + + // deserializing json + var cfg, orig interface{} + var exists, isMap bool + cfgMap := make(map[string]interface{}) + + err = json.Unmarshal(buf, &orig) + cfg = orig + if err != nil { + return err + } + + for i := 0; i < len(attrs); i++ { + cfgMap, isMap = cfg.(map[string]interface{}) + // curs = append(curs, cfgMap) + if !isMap { + return errors.New(fmt.Sprintf("%s has no attributes", strings.Join(attrs[:i], "."))) + } + cfg, exists = cfgMap[attrs[i]] + if !exists { + return errors.New(fmt.Sprintf("Configuration option key \"%s\" not recognized", strings.Join(attrs[:i+1], "."))) + } + } + cfgMap[attrs[len(attrs)-1]] = assignee + buf, err = json.MarshalIndent(orig, "", " ") + if err != nil { + return err + } + WriteFile(filename, buf) + return nil +}