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