github.com/comwrg/go/src@v0.0.0-20220319063731-c238d0440370/runtime/env_plan9.go (about) 1 // Copyright 2012 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 package runtime 6 7 import "unsafe" 8 9 const ( 10 // Plan 9 environment device 11 envDir = "/env/" 12 // size of buffer to read from a directory 13 dirBufSize = 4096 14 // size of buffer to read an environment variable (may grow) 15 envBufSize = 128 16 // offset of the name field in a 9P directory entry - see syscall.UnmarshalDir() 17 nameOffset = 39 18 ) 19 20 // Goenvs caches the Plan 9 environment variables at start of execution into 21 // string array envs, to supply the initial contents for os.Environ. 22 // Subsequent calls to os.Setenv will change this cache, without writing back 23 // to the (possibly shared) Plan 9 environment, so that Setenv and Getenv 24 // conform to the same Posix semantics as on other operating systems. 25 // For Plan 9 shared environment semantics, instead of Getenv(key) and 26 // Setenv(key, value), one can use os.ReadFile("/env/" + key) and 27 // os.WriteFile("/env/" + key, value, 0666) respectively. 28 //go:nosplit 29 func goenvs() { 30 buf := make([]byte, envBufSize) 31 copy(buf, envDir) 32 dirfd := open(&buf[0], _OREAD, 0) 33 if dirfd < 0 { 34 return 35 } 36 defer closefd(dirfd) 37 dofiles(dirfd, func(name []byte) { 38 name = append(name, 0) 39 buf = buf[:len(envDir)] 40 copy(buf, envDir) 41 buf = append(buf, name...) 42 fd := open(&buf[0], _OREAD, 0) 43 if fd < 0 { 44 return 45 } 46 defer closefd(fd) 47 n := len(buf) 48 r := 0 49 for { 50 r = int(pread(fd, unsafe.Pointer(&buf[0]), int32(n), 0)) 51 if r < n { 52 break 53 } 54 n = int(seek(fd, 0, 2)) + 1 55 if len(buf) < n { 56 buf = make([]byte, n) 57 } 58 } 59 if r <= 0 { 60 r = 0 61 } else if buf[r-1] == 0 { 62 r-- 63 } 64 name[len(name)-1] = '=' 65 env := make([]byte, len(name)+r) 66 copy(env, name) 67 copy(env[len(name):], buf[:r]) 68 envs = append(envs, string(env)) 69 }) 70 } 71 72 // Dofiles reads the directory opened with file descriptor fd, applying function f 73 // to each filename in it. 74 //go:nosplit 75 func dofiles(dirfd int32, f func([]byte)) { 76 dirbuf := new([dirBufSize]byte) 77 78 var off int64 = 0 79 for { 80 n := pread(dirfd, unsafe.Pointer(&dirbuf[0]), int32(dirBufSize), off) 81 if n <= 0 { 82 return 83 } 84 for b := dirbuf[:n]; len(b) > 0; { 85 var name []byte 86 name, b = gdirname(b) 87 if name == nil { 88 return 89 } 90 f(name) 91 } 92 off += int64(n) 93 } 94 } 95 96 // Gdirname returns the first filename from a buffer of directory entries, 97 // and a slice containing the remaining directory entries. 98 // If the buffer doesn't start with a valid directory entry, the returned name is nil. 99 //go:nosplit 100 func gdirname(buf []byte) (name []byte, rest []byte) { 101 if 2+nameOffset+2 > len(buf) { 102 return 103 } 104 entryLen, buf := gbit16(buf) 105 if entryLen > len(buf) { 106 return 107 } 108 n, b := gbit16(buf[nameOffset:]) 109 if n > len(b) { 110 return 111 } 112 name = b[:n] 113 rest = buf[entryLen:] 114 return 115 } 116 117 // Gbit16 reads a 16-bit little-endian binary number from b and returns it 118 // with the remaining slice of b. 119 //go:nosplit 120 func gbit16(b []byte) (int, []byte) { 121 return int(b[0]) | int(b[1])<<8, b[2:] 122 }