diff options
Diffstat (limited to 'src/runtime/env_plan9.go')
-rw-r--r-- | src/runtime/env_plan9.go | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/src/runtime/env_plan9.go b/src/runtime/env_plan9.go new file mode 100644 index 0000000..f1ac476 --- /dev/null +++ b/src/runtime/env_plan9.go @@ -0,0 +1,122 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import "unsafe" + +const ( + // Plan 9 environment device + envDir = "/env/" + // size of buffer to read from a directory + dirBufSize = 4096 + // size of buffer to read an environment variable (may grow) + envBufSize = 128 + // offset of the name field in a 9P directory entry - see syscall.UnmarshalDir() + nameOffset = 39 +) + +// Goenvs caches the Plan 9 environment variables at start of execution into +// string array envs, to supply the initial contents for os.Environ. +// Subsequent calls to os.Setenv will change this cache, without writing back +// to the (possibly shared) Plan 9 environment, so that Setenv and Getenv +// conform to the same Posix semantics as on other operating systems. +// For Plan 9 shared environment semantics, instead of Getenv(key) and +// Setenv(key, value), one can use os.ReadFile("/env/" + key) and +// os.WriteFile("/env/" + key, value, 0666) respectively. +//go:nosplit +func goenvs() { + buf := make([]byte, envBufSize) + copy(buf, envDir) + dirfd := open(&buf[0], _OREAD, 0) + if dirfd < 0 { + return + } + defer closefd(dirfd) + dofiles(dirfd, func(name []byte) { + name = append(name, 0) + buf = buf[:len(envDir)] + copy(buf, envDir) + buf = append(buf, name...) + fd := open(&buf[0], _OREAD, 0) + if fd < 0 { + return + } + defer closefd(fd) + n := len(buf) + r := 0 + for { + r = int(pread(fd, unsafe.Pointer(&buf[0]), int32(n), 0)) + if r < n { + break + } + n = int(seek(fd, 0, 2)) + 1 + if len(buf) < n { + buf = make([]byte, n) + } + } + if r <= 0 { + r = 0 + } else if buf[r-1] == 0 { + r-- + } + name[len(name)-1] = '=' + env := make([]byte, len(name)+r) + copy(env, name) + copy(env[len(name):], buf[:r]) + envs = append(envs, string(env)) + }) +} + +// Dofiles reads the directory opened with file descriptor fd, applying function f +// to each filename in it. +//go:nosplit +func dofiles(dirfd int32, f func([]byte)) { + dirbuf := new([dirBufSize]byte) + + var off int64 = 0 + for { + n := pread(dirfd, unsafe.Pointer(&dirbuf[0]), int32(dirBufSize), off) + if n <= 0 { + return + } + for b := dirbuf[:n]; len(b) > 0; { + var name []byte + name, b = gdirname(b) + if name == nil { + return + } + f(name) + } + off += int64(n) + } +} + +// Gdirname returns the first filename from a buffer of directory entries, +// and a slice containing the remaining directory entries. +// If the buffer doesn't start with a valid directory entry, the returned name is nil. +//go:nosplit +func gdirname(buf []byte) (name []byte, rest []byte) { + if 2+nameOffset+2 > len(buf) { + return + } + entryLen, buf := gbit16(buf) + if entryLen > len(buf) { + return + } + n, b := gbit16(buf[nameOffset:]) + if n > len(b) { + return + } + name = b[:n] + rest = buf[entryLen:] + return +} + +// Gbit16 reads a 16-bit little-endian binary number from b and returns it +// with the remaining slice of b. +//go:nosplit +func gbit16(b []byte) (int, []byte) { + return int(b[0]) | int(b[1])<<8, b[2:] +} |