diff --git a/README.md b/README.md index 71bf708e8..721f06a16 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,8 @@ Get a released version on: https://github.com/moul/advanced-ssh-config/releases ### master (unreleased) +* Fix: Allow `$(...)` syntax in the `ResolveCommand` function ([#117](https://github.com/moul/advanced-ssh-config/issues/117)) +* Printing the error of a failing `ResolveCommand` ([#117](https://github.com/moul/advanced-ssh-config/issues/117)) * Fix: `Gateways` field is no longer ignored when the `HostName` field is present ([#102](https://github.com/moul/advanced-ssh-config/issues/102)) * Ignore SIGHUP, close goroutines and export written bytes ([#112](https://github.com/moul/advanced-ssh-config/pull/112)) ([@QuentinPerez](https://github.com/QuentinPerez)) * Various documentation improvements ([@ashmatadeen](https://github.com/ashmatadeen), [@loliee](https://github.com/loliee), [@cerisier](https://github.com/cerisier)) diff --git a/pkg/commands/proxy.go b/pkg/commands/proxy.go index d6da67f85..88a5b1cf1 100644 --- a/pkg/commands/proxy.go +++ b/pkg/commands/proxy.go @@ -1,6 +1,7 @@ package commands import ( + "bytes" "fmt" "io" "net" @@ -178,12 +179,17 @@ func hostPrepare(host *config.Host) error { return err } - out, err := exec.Command(args[0], args[1:]...).Output() - if err != nil { + cmd := exec.Command(args[0], args[1:]...) + var stdout bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + if err := cmd.Run(); err != nil { + Logger.Errorf("ResolveCommand failed: %s", stderr.String()) return err } - host.HostName = strings.TrimSpace(fmt.Sprintf("%s", out)) + host.HostName = strings.TrimSpace(fmt.Sprintf("%s", stdout.String())) Logger.Debugf("Resolved host is: %s", host.HostName) } return nil diff --git a/pkg/config/imported.go b/pkg/config/imported.go new file mode 100644 index 000000000..848dc72ff --- /dev/null +++ b/pkg/config/imported.go @@ -0,0 +1,51 @@ +// This file contains imported functions +// The license and copyright is reported for each functions in the comments. + +package config + +// Imported and unmodified from https://golang.org/src/os/env.go +// Function under the BSD-License - Copyrighted by the Go Authors +// isShellSpecialVar reports whether the character identifies a special +// shell variable such as $*. +func isShellSpecialVar(c uint8) bool { + switch c { + case '*', '#', '$', '@', '!', '?', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return true + } + return false +} + +// Imported and unmodified from https://golang.org/src/os/env.go +// Function under the BSD-License - Copyrighted by the Go Authors +// isAlphaNum reports whether the byte is an ASCII letter, number, or underscore +func isAlphaNum(c uint8) bool { + return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' +} + +// Imported and unmodified from https://golang.org/src/os/env.go +// Function under the BSD-License - Copyrighted by the Go Authors +// getShellName returns the name that begins the string and the number of bytes +// consumed to extract it. If the name is enclosed in {}, it's part of a ${} +// expansion and two more bytes are needed than the length of the name. +func getShellName(s string) (string, int) { + switch { + case s[0] == '{': + if len(s) > 2 && isShellSpecialVar(s[1]) && s[2] == '}' { + return s[1:2], 3 + } + // Scan to closing brace + for i := 1; i < len(s); i++ { + if s[i] == '}' { + return s[1:i], i + 1 + } + } + return "", 1 // Bad syntax; just eat the brace. + case isShellSpecialVar(s[0]): + return s[0:1], 1 + } + // Scan alphanumerics. + var i int + for i = 0; i < len(s) && isAlphaNum(s[i]); i++ { + } + return s[:i], i +} diff --git a/pkg/config/utils.go b/pkg/config/utils.go index 805135089..96f111c15 100644 --- a/pkg/config/utils.go +++ b/pkg/config/utils.go @@ -17,9 +17,29 @@ func GetHomeDir() string { return "" } +// ExpandEnvSafe replaces ${var} or $var in the string according to the values +// of the current environment variables. +// As opposed to os.ExpandEnv, ExpandEnvSafe won't remove the dollar in '$(...)' +// See https://golang.org/src/os/env.go?s=963:994#L22 for the original function +func ExpandEnvSafe(s string) string { + buf := make([]byte, 0, 2*len(s)) + i := 0 + for j := 0; j < len(s); j++ { + // the following line is the only one changing + if s[j] == '$' && j+1 < len(s) && s[j+1] != '(' { + buf = append(buf, s[i:j]...) + name, w := getShellName(s[j+1:]) + buf = append(buf, os.Getenv(name)...) + j += w + i = j + 1 + } + } + return string(buf) + s[i:] +} + func expandUser(path string) (string, error) { // Expand variables - path = os.ExpandEnv(path) + path = ExpandEnvSafe(path) if path[:2] == "~/" { homeDir := GetHomeDir() @@ -37,5 +57,5 @@ func expandField(input string) string { if input == "" { return "" } - return os.ExpandEnv(input) + return ExpandEnvSafe(input) }