github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/envutil/env.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package envutil 12 13 import ( 14 "bytes" 15 "fmt" 16 "os" 17 "os/user" 18 "runtime" 19 "strconv" 20 "strings" 21 "time" 22 23 "github.com/cockroachdb/cockroach/pkg/util/humanizeutil" 24 "github.com/cockroachdb/cockroach/pkg/util/syncutil" 25 ) 26 27 type envVarInfo struct { 28 consumer string 29 present bool 30 value string 31 } 32 33 var envVarRegistry struct { 34 mu syncutil.Mutex 35 cache map[string]envVarInfo 36 } 37 38 func init() { 39 ClearEnvCache() 40 } 41 42 func checkVarName(name string) { 43 // Env vars must: 44 // - start with COCKROACH_ 45 // - be uppercase 46 // - only contain letters, digits, and _ 47 valid := strings.HasPrefix(name, "COCKROACH_") 48 for i := 0; valid && i < len(name); i++ { 49 c := name[i] 50 valid = ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') 51 } 52 if !valid { 53 panic("invalid env var name " + name) 54 } 55 } 56 57 // getEnv retrieves an environment variable, keeps track of where 58 // it was accessed, and checks that each environment variable is accessed 59 // from at most one place. 60 // The bookkeeping enables a report of all influential environment 61 // variables with "cockroach debug env". To keep this report useful, 62 // all relevant environment variables should be read during start up. 63 func getEnv(varName string, depth int) (string, bool) { 64 _, consumer, _, _ := runtime.Caller(depth + 1) 65 checkVarName(varName) 66 67 envVarRegistry.mu.Lock() 68 defer envVarRegistry.mu.Unlock() 69 70 if f, ok := envVarRegistry.cache[varName]; ok { 71 if f.consumer != consumer { 72 panic("environment variable " + varName + " already used from " + f.consumer) 73 } 74 return f.value, f.present 75 } 76 v, found := os.LookupEnv(varName) 77 envVarRegistry.cache[varName] = envVarInfo{consumer: consumer, present: found, value: v} 78 return v, found 79 } 80 81 // ClearEnvCache clears saved environment values so that 82 // a new read access the environment again. (Used for testing) 83 func ClearEnvCache() { 84 envVarRegistry.mu.Lock() 85 defer envVarRegistry.mu.Unlock() 86 87 envVarRegistry.cache = make(map[string]envVarInfo) 88 } 89 90 // GetEnvReport dumps all configuration variables that may have been 91 // used and their value. 92 func GetEnvReport() string { 93 envVarRegistry.mu.Lock() 94 defer envVarRegistry.mu.Unlock() 95 96 var b bytes.Buffer 97 for k, v := range envVarRegistry.cache { 98 if v.present { 99 fmt.Fprintf(&b, "%s = %s # %s\n", k, v.value, v.consumer) 100 } else { 101 fmt.Fprintf(&b, "# %s is not set (read from %s)\n", k, v.consumer) 102 } 103 } 104 return b.String() 105 } 106 107 // GetEnvVarsUsed returns the names of all environment variables that 108 // may have been used. 109 func GetEnvVarsUsed() []string { 110 envVarRegistry.mu.Lock() 111 defer envVarRegistry.mu.Unlock() 112 113 var vars []string 114 for k, v := range envVarRegistry.cache { 115 if v.present { 116 vars = append(vars, k+"="+os.Getenv(k)) 117 } 118 } 119 120 for _, name := range []string{"GOGC", "GODEBUG", "GOMAXPROCS", "GOTRACEBACK"} { 121 if val, ok := os.LookupEnv(name); ok { 122 vars = append(vars, name+"="+val) 123 } 124 } 125 return vars 126 } 127 128 // GetShellCommand returns a complete command to run with a prefix of the command line. 129 func GetShellCommand(cmd string) []string { 130 if runtime.GOOS == "windows" { 131 if shell := os.Getenv("COMSPEC"); len(shell) > 0 { 132 return []string{shell, "/C", cmd} 133 } 134 return []string{`C:\Windows\system32\cmd.exe`, "/C", cmd} 135 } 136 if shell := os.Getenv("SHELL"); len(shell) > 0 { 137 return []string{shell, "-c", cmd} 138 } 139 140 return []string{"/bin/sh", "-c", cmd} 141 } 142 143 // HomeDir returns the user's home directory, as determined by the env 144 // var HOME, if it exists, and otherwise the system's idea of the user 145 // configuration (e.g. on non-UNIX systems). 146 func HomeDir() (string, error) { 147 if homeDir := os.Getenv("HOME"); len(homeDir) > 0 { 148 return homeDir, nil 149 } 150 userAcct, err := user.Current() 151 if err != nil { 152 return "", err 153 } 154 return userAcct.HomeDir, nil 155 } 156 157 // EnvString returns the value set by the specified environment variable. The 158 // depth argument indicates the stack depth of the caller that should be 159 // associated with the variable. 160 // The returned boolean flag indicates if the variable is set. 161 func EnvString(name string, depth int) (string, bool) { 162 return getEnv(name, depth+1) 163 } 164 165 // EnvOrDefaultString returns the value set by the specified 166 // environment variable, if any, otherwise the specified default 167 // value. 168 func EnvOrDefaultString(name string, value string) string { 169 if v, present := getEnv(name, 1); present { 170 return v 171 } 172 return value 173 } 174 175 // EnvOrDefaultBool returns the value set by the specified environment 176 // variable, if any, otherwise the specified default value. 177 func EnvOrDefaultBool(name string, value bool) bool { 178 if str, present := getEnv(name, 1); present { 179 v, err := strconv.ParseBool(str) 180 if err != nil { 181 panic(fmt.Sprintf("error parsing %s: %s", name, err)) 182 } 183 return v 184 } 185 return value 186 } 187 188 // EnvOrDefaultInt returns the value set by the specified environment 189 // variable, if any, otherwise the specified default value. 190 func EnvOrDefaultInt(name string, value int) int { 191 if str, present := getEnv(name, 1); present { 192 v, err := strconv.ParseInt(str, 0, 0) 193 if err != nil { 194 panic(fmt.Sprintf("error parsing %s: %s", name, err)) 195 } 196 return int(v) 197 } 198 return value 199 } 200 201 // EnvOrDefaultInt64 returns the value set by the specified environment 202 // variable, if any, otherwise the specified default value. 203 func EnvOrDefaultInt64(name string, value int64) int64 { 204 if str, present := getEnv(name, 1); present { 205 v, err := strconv.ParseInt(str, 0, 64) 206 if err != nil { 207 panic(fmt.Sprintf("error parsing %s: %s", name, err)) 208 } 209 return v 210 } 211 return value 212 } 213 214 // EnvOrDefaultFloat64 returns the value set by the specified environment 215 // variable, if any, otherwise the specified default value. 216 func EnvOrDefaultFloat64(name string, value float64) float64 { 217 if str, present := getEnv(name, 1); present { 218 v, err := strconv.ParseFloat(str, 64) 219 if err != nil { 220 panic(fmt.Sprintf("error parsing %s: %s", name, err)) 221 } 222 return v 223 } 224 return value 225 } 226 227 var _ = EnvOrDefaultFloat64 // silence unused warning 228 229 // EnvOrDefaultBytes returns the value set by the specified environment 230 // variable, if any, otherwise the specified default value. 231 func EnvOrDefaultBytes(name string, value int64) int64 { 232 if str, present := getEnv(name, 1); present { 233 v, err := humanizeutil.ParseBytes(str) 234 if err != nil { 235 panic(fmt.Sprintf("error parsing %s: %s", name, err)) 236 } 237 return v 238 } 239 return value 240 } 241 242 // EnvOrDefaultDuration returns the value set by the specified environment 243 // variable, if any, otherwise the specified default value. 244 func EnvOrDefaultDuration(name string, value time.Duration) time.Duration { 245 if str, present := getEnv(name, 1); present { 246 v, err := time.ParseDuration(str) 247 if err != nil { 248 panic(fmt.Sprintf("error parsing %s: %s", name, err)) 249 } 250 return v 251 } 252 return value 253 }