gitee.com/quant1x/gox@v1.21.2/util/homedir/homedir.go (about) 1 package homedir 2 3 import ( 4 "bytes" 5 "errors" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "runtime" 10 "strconv" 11 "strings" 12 "sync" 13 ) 14 15 // DisableCache will disable caching of the home directory. Caching is enabled 16 // by default. 17 var DisableCache bool 18 19 var homedirCache string 20 var cacheLock sync.RWMutex 21 22 // Dir returns the home directory for the executing user. 23 // 24 // This uses an OS-specific method for discovering the home directory. 25 // An error is returned if a home directory cannot be detected. 26 func Dir() (string, error) { 27 if !DisableCache { 28 cacheLock.RLock() 29 cached := homedirCache 30 cacheLock.RUnlock() 31 if cached != "" { 32 return cached, nil 33 } 34 } 35 36 cacheLock.Lock() 37 defer cacheLock.Unlock() 38 39 var result string 40 var err error 41 if runtime.GOOS == "windows" { 42 result, err = dirWindows() 43 } else { 44 // Unix-like system, so just assume Unix 45 result, err = dirUnix() 46 } 47 48 if err != nil { 49 return "", err 50 } 51 homedirCache = result 52 return result, nil 53 } 54 55 // Expand expands the path to include the home directory if the path 56 // is prefixed with `~`. If it isn't prefixed with `~`, the path is 57 // returned as-is. 58 func Expand(path string) (string, error) { 59 if len(path) == 0 { 60 return path, nil 61 } 62 63 if path[0] != '~' { 64 return path, nil 65 } 66 67 if len(path) > 1 && path[1] != '/' && path[1] != '\\' { 68 return "", errors.New("cannot expand user-specific home dir") 69 } 70 71 dir, err := Dir() 72 if err != nil { 73 return "", err 74 } 75 76 return filepath.Join(dir, path[1:]), nil 77 } 78 79 // Reset clears the cache, forcing the next call to Dir to re-detect 80 // the home directory. This generally never has to be called, but can be 81 // useful in tests if you're modifying the home directory via the HOME 82 // env var or something. 83 func Reset() { 84 cacheLock.Lock() 85 defer cacheLock.Unlock() 86 homedirCache = "" 87 } 88 89 const ( 90 EnvGoxHome = "GOX_HOME" 91 ) 92 93 func dirUnix() (string, error) { 94 if home := os.Getenv(EnvGoxHome); home != "" { 95 return home, nil 96 } 97 homeEnv := "HOME" 98 if runtime.GOOS == "plan9" { 99 // On plan9, env vars are lowercase. 100 homeEnv = "home" 101 } 102 103 // First prefer the HOME environmental variable 104 if home := os.Getenv(homeEnv); home != "" { 105 return home, nil 106 } 107 108 var stdout bytes.Buffer 109 110 // If that fails, try OS specific commands 111 if runtime.GOOS == "darwin" { 112 cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`) 113 cmd.Stdout = &stdout 114 if err := cmd.Run(); err == nil { 115 result := strings.TrimSpace(stdout.String()) 116 if result != "" { 117 return result, nil 118 } 119 } 120 } else { 121 cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) 122 cmd.Stdout = &stdout 123 if err := cmd.Run(); err != nil { 124 // If the error is ErrNotFound, we ignore it. Otherwise, return it. 125 if err != exec.ErrNotFound { 126 return "", err 127 } 128 } else { 129 if passwd := strings.TrimSpace(stdout.String()); passwd != "" { 130 // username:password:uid:gid:gecos:home:shell 131 passwdParts := strings.SplitN(passwd, ":", 7) 132 if len(passwdParts) > 5 { 133 return passwdParts[5], nil 134 } 135 } 136 } 137 } 138 139 // If all else fails, try the shell 140 stdout.Reset() 141 cmd := exec.Command("sh", "-c", "cd && pwd") 142 cmd.Stdout = &stdout 143 if err := cmd.Run(); err != nil { 144 return "", err 145 } 146 147 result := strings.TrimSpace(stdout.String()) 148 if result == "" { 149 return "", errors.New("blank output when reading home directory") 150 } 151 152 return result, nil 153 } 154 155 func dirWindows() (string, error) { 156 // 启用环境变量GOX_HOME是为了Windows服务以系统账户运行时无法获取登录用户的宿主目录而预备的 157 if home := os.Getenv(EnvGoxHome); home != "" { 158 return home, nil 159 } 160 // First prefer the HOME environmental variable 161 if home := os.Getenv("HOME"); home != "" { 162 return home, nil 163 } 164 165 // Prefer standard environment variable USERPROFILE 166 if home := os.Getenv("USERPROFILE"); home != "" { 167 return home, nil 168 } 169 170 drive := os.Getenv("HOMEDRIVE") 171 path := os.Getenv("HOMEPATH") 172 home := drive + path 173 if drive == "" || path == "" { 174 return "", errors.New("HOMEDRIVE, HOMEPATH, or USERPROFILE are blank") 175 } 176 177 return home, nil 178 }