1// Package exepath provides information on the path used to invoke the running 2// program. 3// 4// Relativity 5// 6// This package is distinct from other packages providing similar functionality 7// because the path it provides is not realpath'd. That is, if a binary was 8// executed via a symlink, the path expressed is still expressed in terms of 9// that symlink. This is useful if you wish to use relative paths. 10// 11// For example: 12// 13// /here/foo/ 14// bin: symlink to /somewhere/else/bin 15// data.txt 16// 17// /somewhere/else/bin 18// foo 19// 20// /here/foo$ ./bin/foo 21// 22// Here Abs in this package will specify /here/foo/bin/foo, meaning that 23// Join(Dir(Abs), "..") leads to /here/foo, not /somewhere/else, allowing 24// easy access to data.txt. 25package exepath // import "gopkg.in/hlandau/svcutils.v1/exepath" 26 27import ( 28 "os" 29 "os/exec" 30 "path/filepath" 31 "strings" 32) 33 34// Absolute path to EXE which was invoked. This is set at init()-time. 35// 36// This path is not realpath'd — see package documentation. 37var Abs string 38 39func getRawPath() string { 40 // "github.com/kardianos/osext".Executable looks nice, but may return the 41 // realpath of the path because this is how the kernel returns it as 42 // /proc/self/exe. This causes problems with layouts like 43 // 44 // some-work-directory/ 45 // bin/ -> symlink to $GOPATH/bin 46 // src/ -> symlink to $GOPATH/src 47 // etc/ 48 // ... configuration files ... 49 // 50 // where bin/foo is executed from some-work-directory and expects to find files in etc/. 51 // Since -fork reexecutes with the exepath.Abs path, this prevents paths like 52 // $BIN/../etc/foo.conf from working (where $BIN is the dir of the executable path). 53 // 54 // Okay, maybe this is a byzantine configuration. But still, this breaks my existing 55 // configuration, so I'm sticking with os.Args[0] for now, as -fork should be as seamless 56 // as possible to relying applications. 57 58 return os.Args[0] 59} 60 61func init() { 62 rawPath := getRawPath() 63 64 // If there are no separators in rawPath, we've presumably been invoked from the path 65 // and should qualify the path accordingly. 66 idx := strings.IndexFunc(rawPath, func(r rune) bool { 67 return r == '/' || r == filepath.Separator 68 }) 69 if idx < 0 { 70 abs, err := exec.LookPath(rawPath) 71 if err != nil { 72 return 73 } 74 75 Abs = abs 76 } else { 77 abs, err := filepath.Abs(rawPath) 78 if err != nil { 79 return 80 } 81 82 Abs = abs 83 } 84 85 initProgramName() 86} 87