diff --git a/free/main.go b/free/main.go index d1c8a43..86568a3 100644 --- a/free/main.go +++ b/free/main.go @@ -95,7 +95,7 @@ func main() { flag.Parse() if !*jsonModePtr { - fmt.Printf("%s", jsh.Fallback("/usr/bin/free")) + fmt.Printf("%s", jsh.Fallback("free")) } else { runJsonMode() } diff --git a/free/main_test.go b/free/main_test.go index c60e170..c09603e 100644 --- a/free/main_test.go +++ b/free/main_test.go @@ -2,8 +2,8 @@ package main import ( "jsh" - "testing" "reflect" + "testing" ) func testUnitError(expected jsh.Unit, t *testing.T) { @@ -45,14 +45,14 @@ func TestConvertBadUnit(t *testing.T) { func TestParseGoodLine(t *testing.T) { goodLine := "MemTotal: 16370344 kB" - expectedStat := jsh.MemStat { 16370344, jsh.KB } + expectedStat := jsh.MemStat{16370344, jsh.KB} expectedKey := "MemTotal" key, stat, err := ParseLine(goodLine) if err != nil { t.Error(err) } if key != expectedKey { - t.Errorf("Expected key %s, got key %s", expectedKey, key) + t.Errorf("Expected key %s, got key %s", expectedKey, key) } if !reflect.DeepEqual(expectedStat, stat) { t.Error("Expected stat ", expectedStat, " got ", stat) diff --git a/ps/main.go b/ps/main.go index 9df6a7b..89fc173 100644 --- a/ps/main.go +++ b/ps/main.go @@ -25,7 +25,7 @@ func PsOutputToProcesses(out string) *[]jsh.Process { func runJsonMode() { // Run procps-ng "ps" with full output - psOut := string(*jsh.FallbackWithArgs("/usr/bin/ps", []string{"auxww"})) + psOut := string(*jsh.FallbackWithArgs("ps", []string{"auxww"})) processesPtr := PsOutputToProcesses(psOut) frame := jsh.JshFrame{*processesPtr, []string{}} @@ -43,7 +43,7 @@ func main() { flag.Parse() if !*jsonModePtr { - fmt.Printf("%s", jsh.Fallback("/usr/bin/ps")) + fmt.Printf("%s", jsh.Fallback("ps")) } else { runJsonMode() } diff --git a/utils.go b/utils.go index 7f6561e..634938a 100644 --- a/utils.go +++ b/utils.go @@ -1,9 +1,12 @@ package jsh import ( + "errors" + "fmt" "math" "os" "os/exec" + "strings" "unicode" ) @@ -52,7 +55,13 @@ func FallbackWithArgs(program string, args []string) *[]byte { if args == nil { args = os.Args[1:] } - out, err := exec.Command(program, args...).Output() + executable, err := findExecutable(program) + + if err != nil { + panic(err) + } + + out, err := exec.Command(executable, args...).Output() if err != nil { panic(err) } @@ -62,3 +71,58 @@ func FallbackWithArgs(program string, args []string) *[]byte { func Fallback(program string) *[]byte { return FallbackWithArgs(program, nil) } + +// Finds a given executable on the system path, returning the absolute path the program if found, and an error +// if it's not found +func findExecutable(programName string) (string, error) { + // First, find our binary location + binLocWithName, err := os.Readlink("/proc/self/exe") + if err != nil { + return "", err + } + // Get our binary name and remove it from the binary path, leaving just our binary location + // Also trim the extra path separator + binName := os.Args[0] + binLoc := strings.TrimSuffix(binLocWithName, binName) + binLoc = strings.TrimSuffix(binLoc, string(os.PathSeparator)) + + // Get the system path and split it by the system path separator + pathString := os.Getenv("PATH") + path := strings.Split(pathString, string(os.PathListSeparator)) + + // Loop until we find our location on the path, so we know where to start looking after for the + // next implementation of the given program name + var foundPath bool + var pathNum int + for index, pathEl := range path { + // Trim the path separator from this element, if it has it + pathEl = strings.TrimSuffix(pathEl, string(os.PathSeparator)) + if pathEl == binLoc { + foundPath = true + pathNum = index + break + } + } + + // If we found our path on the real path, then search the remaining path elements. Otherwise, search all elements + if foundPath { + return searchPath(programName, path[pathNum+1:len(path)-1]) + } else { + return searchPath(programName, path) + } +} + +// Searches the given set of paths for a the given program, returing when it finds it or returning an error if the +// program is not found +func searchPath(programName string, path []string) (string, error) { + // Loop through each path element, test for the existance of the file, and then return if we find it + for _, pathEl := range path { + fullProgram := pathEl + string(os.PathSeparator) + programName + _, err := os.Stat(fullProgram) + if err == nil { + return fullProgram, nil + } + } + + return "", errors.New(fmt.Sprintf("Could not find program %s on the PATH: %s", programName, path)) +}