about summary refs log tree commit diff
path: root/main/civisibility/utils/home.go
blob: 8625010febfd2d14b7dd534e9d11978e4f292fc4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2024 Datadog, Inc.

package utils

import (
	"bytes"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

// This code is based on: https://github.com/mitchellh/go-homedir/blob/v1.1.0/homedir.go (MIT License)

// ExpandPath expands a file path that starts with '~' to the user's home directory.
// If the path does not start with '~', it is returned unchanged.
//
// Parameters:
//
//	path - The file path to be expanded.
//
// Returns:
//
//	The expanded file path, with '~' replaced by the user's home directory, if applicable.
func ExpandPath(path string) string {
	if len(path) == 0 || path[0] != '~' {
		return path
	}

	// If the second character is not '/' or '\', return the path unchanged
	if len(path) > 1 && path[1] != '/' && path[1] != '\\' {
		return path
	}

	homeFolder := getHomeDir()
	if len(homeFolder) > 0 {
		return filepath.Join(homeFolder, path[1:])
	}

	return path
}

// getHomeDir returns the home directory of the current user.
// The method used to determine the home directory depends on the operating system.
//
// On Windows, it prefers the HOME environment variable, then USERPROFILE, and finally combines HOMEDRIVE and HOMEPATH.
// On Unix-like systems, it prefers the HOME environment variable, and falls back to various shell commands
// to determine the home directory if necessary.
//
// Returns:
//
//	The home directory of the current user.
func getHomeDir() string {
	if runtime.GOOS == "windows" {
		if home := os.Getenv("HOME"); home != "" {
			// First prefer the HOME environment variable
			return home
		}
		if userProfile := os.Getenv("USERPROFILE"); userProfile != "" {
			// Prefer the USERPROFILE environment variable
			return userProfile
		}

		homeDrive := os.Getenv("HOMEDRIVE")
		homePath := os.Getenv("HOMEPATH")
		return homeDrive + homePath
	}

	homeEnv := "HOME"
	if runtime.GOOS == "plan9" {
		// On plan9, environment variables are lowercase.
		homeEnv = "home"
	}

	if home := os.Getenv(homeEnv); home != "" {
		// Prefer the HOME environment variable
		return home
	}

	var stdout bytes.Buffer
	if runtime.GOOS == "darwin" {
		// On macOS, use dscl to read the NFSHomeDirectory
		cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`)
		cmd.Stdout = &stdout
		if err := cmd.Run(); err == nil {
			result := strings.TrimSpace(stdout.String())
			if result != "" {
				return result
			}
		}
	} else {
		// On other Unix-like systems, use getent to read the passwd entry for the current user
		cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid()))
		cmd.Stdout = &stdout
		if err := cmd.Run(); err == nil {
			if passwd := strings.TrimSpace(stdout.String()); passwd != "" {
				// The passwd entry is in the format: username:password:uid:gid:gecos:home:shell
				passwdParts := strings.SplitN(passwd, ":", 7)
				if len(passwdParts) > 5 {
					return passwdParts[5]
				}
			}
		}
	}

	// If all else fails, use the shell to determine the home directory
	stdout.Reset()
	cmd := exec.Command("sh", "-c", "cd && pwd")
	cmd.Stdout = &stdout
	if err := cmd.Run(); err == nil {
		return strings.TrimSpace(stdout.String())
	}

	return ""
}