github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/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 // 29 //go:nosplit 30 func goenvs() { 31 buf := make([]byte, envBufSize) 32 copy(buf, envDir) 33 dirfd := open(&buf[0], _OREAD, 0) 34 if dirfd < 0 { 35 return 36 } 37 defer closefd(dirfd) 38 dofiles(dirfd, func(name []byte) { 39 name = append(name, 0) 40 buf = buf[:len(envDir)] 41 copy(buf, envDir) 42 buf = append(buf, name...) 43 fd := open(&buf[0], _OREAD, 0) 44 if fd < 0 { 45 return 46 } 47 defer closefd(fd) 48 n := len(buf) 49 r := 0 50 for { 51 r = int(pread(fd, unsafe.Pointer(&buf[0]), int32(n), 0)) 52 if r < n { 53 break 54 } 55 n = int(seek(fd, 0, 2)) + 1 56 if len(buf) < n { 57 buf = make([]byte, n) 58 } 59 } 60 if r <= 0 { 61 r = 0 62 } else if buf[r-1] == 0 { 63 r-- 64 } 65 name[len(name)-1] = '=' 66 env := make([]byte, len(name)+r) 67 copy(env, name) 68 copy(env[len(name):], buf[:r]) 69 envs = append(envs, string(env)) 70 }) 71 } 72 73 // dofiles reads the directory opened with file descriptor fd, applying function f 74 // to each filename in it. 75 // 76 //go:nosplit 77 func dofiles(dirfd int32, f func([]byte)) { 78 dirbuf := new([dirBufSize]byte) 79 80 var off int64 = 0 81 for { 82 n := pread(dirfd, unsafe.Pointer(&dirbuf[0]), int32(dirBufSize), off) 83 if n <= 0 { 84 return 85 } 86 for b := dirbuf[:n]; len(b) > 0; { 87 var name []byte 88 name, b = gdirname(b) 89 if name == nil { 90 return 91 } 92 f(name) 93 } 94 off += int64(n) 95 } 96 } 97 98 // gdirname returns the first filename from a buffer of directory entries, 99 // and a slice containing the remaining directory entries. 100 // If the buffer doesn't start with a valid directory entry, the returned name is nil. 101 // 102 //go:nosplit 103 func gdirname(buf []byte) (name []byte, rest []byte) { 104 if 2+nameOffset+2 > len(buf) { 105 return 106 } 107 entryLen, buf := gbit16(buf) 108 if entryLen > len(buf) { 109 return 110 } 111 n, b := gbit16(buf[nameOffset:]) 112 if n > len(b) { 113 return 114 } 115 name = b[:n] 116 rest = buf[entryLen:] 117 return 118 } 119 120 // gbit16 reads a 16-bit little-endian binary number from b and returns it 121 // with the remaining slice of b. 122 // 123 //go:nosplit 124 func gbit16(b []byte) (int, []byte) { 125 return int(b[0]) | int(b[1])<<8, b[2:] 126 }