| // 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 |
| } |