mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
Open /dev/bintime at process start on Plan 9, marked close-on-exec, hold it open for the duration of the process, and use it for obtaining time. The change to using /dev/bintime also sets up for an upcoming Plan 9 change to add monotonic time to that file. If the monotonic field is available, then nanotime1 and time.now use that field. Otherwise they fall back to using Unix nanoseconds as "monotonic", as they always have. Before this CL, monotonic time went backward any time aux/timesync decided to adjust the system's time-of-day backward. Also use /dev/random for randomness (once at startup). Before this CL, there was no real randomness in the runtime on Plan 9 (the crypto/rand package still had some). Now there will be. Change-Id: I0c20ae79d3d96eff1a5f839a56cec5c4bc517e61 Reviewed-on: https://go-review.googlesource.com/c/go/+/656755 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Bypass: Russ Cox <rsc@golang.org> Reviewed-by: Carlos Amedee <carlos@golang.org>
127 lines
3.0 KiB
Go
127 lines
3.0 KiB
Go
// 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|_OCEXEC, 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|_OCEXEC, 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:]
|
|
}
|