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  }