// Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || plan9 // Unix environment variables. package syscall import ( "runtime" "sync" ) var ( // envOnce guards initialization by copyenv, which populates env. envOnce sync.Once // envLock guards env and envs. envLock sync.RWMutex // env maps from an environment variable to its first occurrence in envs. env map[string]int // envs is provided by the runtime. elements are expected to // be of the form "key=value". An empty string means deleted // (or a duplicate to be ignored). envs []string = runtime_envs() ) func runtime_envs() []string // in package runtime // setenv_c and unsetenv_c are provided by the runtime but are no-ops // if cgo isn't loaded. func setenv_c(k, v string) func unsetenv_c(k string) func copyenv() { env = make(map[string]int) for i, s := range envs { for j := 0; j < len(s); j++ { if s[j] == '=' { key := s[:j] if _, ok := env[key]; !ok { env[key] = i // first mention of key } else { // Clear duplicate keys. This permits Unsetenv to // safely delete only the first item without // worrying about unshadowing a later one, // which might be a security problem. envs[i] = "" } break } } } } func Unsetenv(key string) error { envOnce.Do(copyenv) envLock.Lock() defer envLock.Unlock() if i, ok := env[key]; ok { envs[i] = "" delete(env, key) } unsetenv_c(key) return nil } func Getenv(key string) (value string, found bool) { envOnce.Do(copyenv) if len(key) == 0 { return "", false } envLock.RLock() defer envLock.RUnlock() i, ok := env[key] if !ok { return "", false } s := envs[i] for i := 0; i < len(s); i++ { if s[i] == '=' { return s[i+1:], true } } return "", false } func Setenv(key, value string) error { envOnce.Do(copyenv) if len(key) == 0 { return EINVAL } for i := 0; i < len(key); i++ { if key[i] == '=' || key[i] == 0 { return EINVAL } } // On Plan 9, null is used as a separator, eg in $path. if runtime.GOOS != "plan9" { for i := 0; i < len(value); i++ { if value[i] == 0 { return EINVAL } } } envLock.Lock() defer envLock.Unlock() i, ok := env[key] kv := key + "=" + value if ok { envs[i] = kv } else { i = len(envs) envs = append(envs, kv) } env[key] = i setenv_c(key, value) return nil } func Clearenv() { envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv envLock.Lock() defer envLock.Unlock() for k := range env { unsetenv_c(k) } env = make(map[string]int) envs = []string{} } func Environ() []string { envOnce.Do(copyenv) envLock.RLock() defer envLock.RUnlock() a := make([]string, 0, len(envs)) for _, env := range envs { if env != "" { a = append(a, env) } } return a }