1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build unix || (js && wasm) || plan9 || wasip1
6
7// Unix environment variables.
8
9package syscall
10
11import (
12	"runtime"
13	"sync"
14)
15
16var (
17	// envOnce guards initialization by copyenv, which populates env.
18	envOnce sync.Once
19
20	// envLock guards env and envs.
21	envLock sync.RWMutex
22
23	// env maps from an environment variable to its first occurrence in envs.
24	env map[string]int
25
26	// envs is provided by the runtime. elements are expected to
27	// be of the form "key=value". An empty string means deleted
28	// (or a duplicate to be ignored).
29	envs []string = runtime_envs()
30)
31
32func runtime_envs() []string // in package runtime
33
34func copyenv() {
35	env = make(map[string]int)
36	for i, s := range envs {
37		for j := 0; j < len(s); j++ {
38			if s[j] == '=' {
39				key := s[:j]
40				if _, ok := env[key]; !ok {
41					env[key] = i // first mention of key
42				} else {
43					// Clear duplicate keys. This permits Unsetenv to
44					// safely delete only the first item without
45					// worrying about unshadowing a later one,
46					// which might be a security problem.
47					envs[i] = ""
48				}
49				break
50			}
51		}
52	}
53}
54
55func Unsetenv(key string) error {
56	envOnce.Do(copyenv)
57
58	envLock.Lock()
59	defer envLock.Unlock()
60
61	if i, ok := env[key]; ok {
62		envs[i] = ""
63		delete(env, key)
64	}
65	runtimeUnsetenv(key)
66	return nil
67}
68
69func Getenv(key string) (value string, found bool) {
70	envOnce.Do(copyenv)
71	if len(key) == 0 {
72		return "", false
73	}
74
75	envLock.RLock()
76	defer envLock.RUnlock()
77
78	i, ok := env[key]
79	if !ok {
80		return "", false
81	}
82	s := envs[i]
83	for i := 0; i < len(s); i++ {
84		if s[i] == '=' {
85			return s[i+1:], true
86		}
87	}
88	return "", false
89}
90
91func Setenv(key, value string) error {
92	envOnce.Do(copyenv)
93	if len(key) == 0 {
94		return EINVAL
95	}
96	for i := 0; i < len(key); i++ {
97		if key[i] == '=' || key[i] == 0 {
98			return EINVAL
99		}
100	}
101	// On Plan 9, null is used as a separator, eg in $path.
102	if runtime.GOOS != "plan9" {
103		for i := 0; i < len(value); i++ {
104			if value[i] == 0 {
105				return EINVAL
106			}
107		}
108	}
109
110	envLock.Lock()
111	defer envLock.Unlock()
112
113	i, ok := env[key]
114	kv := key + "=" + value
115	if ok {
116		envs[i] = kv
117	} else {
118		i = len(envs)
119		envs = append(envs, kv)
120	}
121	env[key] = i
122	runtimeSetenv(key, value)
123	return nil
124}
125
126func Clearenv() {
127	envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv
128
129	envLock.Lock()
130	defer envLock.Unlock()
131
132	for k := range env {
133		runtimeUnsetenv(k)
134	}
135	env = make(map[string]int)
136	envs = []string{}
137}
138
139func Environ() []string {
140	envOnce.Do(copyenv)
141	envLock.RLock()
142	defer envLock.RUnlock()
143	a := make([]string, 0, len(envs))
144	for _, env := range envs {
145		if env != "" {
146			a = append(a, env)
147		}
148	}
149	return a
150}
151