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  }