env_unix.mx raw

   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  
   9  package syscall
  10  
  11  import (
  12  	"runtime"
  13  	"sync"
  14  )
  15  
  16  var (
  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  
  29  func runtime_envs() []string // in package runtime
  30  
  31  var copyenv = sync.OnceFunc(func() {
  32  	env = make(map[string]int)
  33  	for i, s := range envs {
  34  		for j := 0; j < len(s); j++ {
  35  			if s[j] == '=' {
  36  				key := s[:j]
  37  				if _, ok := env[key]; !ok {
  38  					env[key] = i // first mention of key
  39  				} else {
  40  					// Clear duplicate keys. This permits Unsetenv to
  41  					// safely delete only the first item without
  42  					// worrying about unshadowing a later one,
  43  					// which might be a security problem.
  44  					envs[i] = ""
  45  				}
  46  				break
  47  			}
  48  		}
  49  	}
  50  })
  51  
  52  func Unsetenv(key string) error {
  53  	copyenv()
  54  
  55  	envLock.Lock()
  56  	defer envLock.Unlock()
  57  
  58  	if i, ok := env[key]; ok {
  59  		envs[i] = ""
  60  		delete(env, key)
  61  	}
  62  	runtimeUnsetenv(key)
  63  	return nil
  64  }
  65  
  66  func Getenv(key string) (value string, found bool) {
  67  	copyenv()
  68  	if len(key) == 0 {
  69  		return "", false
  70  	}
  71  
  72  	envLock.RLock()
  73  	defer envLock.RUnlock()
  74  
  75  	i, ok := env[key]
  76  	if !ok {
  77  		return "", false
  78  	}
  79  	s := envs[i]
  80  	for i := 0; i < len(s); i++ {
  81  		if s[i] == '=' {
  82  			return s[i+1:], true
  83  		}
  84  	}
  85  	return "", false
  86  }
  87  
  88  func Setenv(key, value string) error {
  89  	copyenv()
  90  	if len(key) == 0 {
  91  		return EINVAL
  92  	}
  93  	for i := 0; i < len(key); i++ {
  94  		if key[i] == '=' || key[i] == 0 {
  95  			return EINVAL
  96  		}
  97  	}
  98  	// On Plan 9, null is used as a separator, eg in $path.
  99  	if runtime.GOOS != "plan9" {
 100  		for i := 0; i < len(value); i++ {
 101  			if value[i] == 0 {
 102  				return EINVAL
 103  			}
 104  		}
 105  	}
 106  
 107  	envLock.Lock()
 108  	defer envLock.Unlock()
 109  
 110  	i, ok := env[key]
 111  	kv := key + "=" + value
 112  	if ok {
 113  		envs[i] = kv
 114  	} else {
 115  		i = len(envs)
 116  		envs = append(envs, kv)
 117  	}
 118  	env[key] = i
 119  	runtimeSetenv(key, value)
 120  	return nil
 121  }
 122  
 123  func Clearenv() {
 124  	copyenv()
 125  
 126  	envLock.Lock()
 127  	defer envLock.Unlock()
 128  
 129  	for k := range env {
 130  		runtimeUnsetenv(k)
 131  	}
 132  	env = make(map[string]int)
 133  	envs = []string{}
 134  }
 135  
 136  func Environ() []string {
 137  	copyenv()
 138  	envLock.RLock()
 139  	defer envLock.RUnlock()
 140  	a := make([]string, 0, len(envs))
 141  	for _, env := range envs {
 142  		if env != "" {
 143  			a = append(a, env)
 144  		}
 145  	}
 146  	return a
 147  }
 148