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 aix || darwin || dragonfly || freebsd || (js && wasm) || linux || netbsd || openbsd || solaris || plan9
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
34// setenv_c and unsetenv_c are provided by the runtime but are no-ops
35// if cgo isn't loaded.
36func setenv_c(k, v string)
37func unsetenv_c(k string)
38
39func copyenv() {
40	env = make(map[string]int)
41	for i, s := range envs {
42		for j := 0; j < len(s); j++ {
43			if s[j] == '=' {
44				key := s[:j]
45				if _, ok := env[key]; !ok {
46					env[key] = i // first mention of key
47				} else {
48					// Clear duplicate keys. This permits Unsetenv to
49					// safely delete only the first item without
50					// worrying about unshadowing a later one,
51					// which might be a security problem.
52					envs[i] = ""
53				}
54				break
55			}
56		}
57	}
58}
59
60func Unsetenv(key string) error {
61	envOnce.Do(copyenv)
62
63	envLock.Lock()
64	defer envLock.Unlock()
65
66	if i, ok := env[key]; ok {
67		envs[i] = ""
68		delete(env, key)
69	}
70	unsetenv_c(key)
71	return nil
72}
73
74func Getenv(key string) (value string, found bool) {
75	envOnce.Do(copyenv)
76	if len(key) == 0 {
77		return "", false
78	}
79
80	envLock.RLock()
81	defer envLock.RUnlock()
82
83	i, ok := env[key]
84	if !ok {
85		return "", false
86	}
87	s := envs[i]
88	for i := 0; i < len(s); i++ {
89		if s[i] == '=' {
90			return s[i+1:], true
91		}
92	}
93	return "", false
94}
95
96func Setenv(key, value string) error {
97	envOnce.Do(copyenv)
98	if len(key) == 0 {
99		return EINVAL
100	}
101	for i := 0; i < len(key); i++ {
102		if key[i] == '=' || key[i] == 0 {
103			return EINVAL
104		}
105	}
106	// On Plan 9, null is used as a separator, eg in $path.
107	if runtime.GOOS != "plan9" {
108		for i := 0; i < len(value); i++ {
109			if value[i] == 0 {
110				return EINVAL
111			}
112		}
113	}
114
115	envLock.Lock()
116	defer envLock.Unlock()
117
118	i, ok := env[key]
119	kv := key + "=" + value
120	if ok {
121		envs[i] = kv
122	} else {
123		i = len(envs)
124		envs = append(envs, kv)
125	}
126	env[key] = i
127	setenv_c(key, value)
128	return nil
129}
130
131func Clearenv() {
132	envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv
133
134	envLock.Lock()
135	defer envLock.Unlock()
136
137	for k := range env {
138		unsetenv_c(k)
139	}
140	env = make(map[string]int)
141	envs = []string{}
142}
143
144func Environ() []string {
145	envOnce.Do(copyenv)
146	envLock.RLock()
147	defer envLock.RUnlock()
148	a := make([]string, 0, len(envs))
149	for _, env := range envs {
150		if env != "" {
151			a = append(a, env)
152		}
153	}
154	return a
155}
156