R=rsc, brainman, ality, r2, r

CC=golang-dev
https://golang.org/cl/3816043
This commit is contained in:
Yuval Pavel Zholkover 2011-04-02 14:24:03 -07:00 committed by Rob Pike
parent c42b3e21c3
commit 1cc4a5cd94
17 changed files with 1604 additions and 6 deletions

View File

@ -32,6 +32,9 @@ GOFILES_linux=\
GOFILES_windows=\
exec_windows.go
GOFILES_plan9=\
exec_plan9.go
OFILES=\
asm_$(GOOS)_$(GOARCH).$O\

View File

@ -0,0 +1,151 @@
// Copyright 2009 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.
//
// System call support for 386, Plan 9
//
//func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err string)
//func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err string)
//func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
//func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
// Trap # in AX, args on stack above caller pc.
TEXT ·Syscall(SB),7,$0
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
LEAL 4(SP), DI
CLD
MOVSL
MOVSL
MOVSL
INT $64
MOVL AX, r1+20(SP)
MOVL $0, r2+24(SP)
CMPL AX, $-1
JNE ok3
SUBL $8, SP
CALL syscall·errstr(SB)
MOVL SP, SI
ADDL $8, SP
JMP copyresult3
ok3:
LEAL runtime·emptystring(SB), SI
copyresult3:
LEAL err+28(SP), DI
CLD
MOVSL
MOVSL
CALL runtime·exitsyscall(SB)
RET
TEXT ·Syscall6(SB),7,$0
CALL runtime·entersyscall(SB)
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
LEAL 4(SP), DI
CLD
MOVSL
MOVSL
MOVSL
MOVSL
MOVSL
MOVSL
INT $64
MOVL AX, r1+32(SP)
MOVL $0, r2+36(SP)
CMPL AX, $-1
JNE ok4
SUBL $8, SP
CALL syscall·errstr(SB)
MOVL SP, SI
ADDL $8, SP
JMP copyresult4
ok4:
LEAL runtime·emptystring(SB), SI
copyresult4:
LEAL err+40(SP), DI
CLD
MOVSL
MOVSL
CALL runtime·exitsyscall(SB)
RET
TEXT ·RawSyscall(SB),7,$0
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
LEAL 4(SP), DI
CLD
MOVSL
MOVSL
MOVSL
INT $64
MOVL AX, r1+20(SP)
MOVL AX, r2+24(SP)
MOVL AX, err+28(SP)
RET
TEXT ·RawSyscall6(SB),7,$0
MOVL 4(SP), AX // syscall entry
// slide args down on top of system call number
LEAL 8(SP), SI
LEAL 4(SP), DI
CLD
MOVSL
MOVSL
MOVSL
MOVSL
MOVSL
MOVSL
INT $64
MOVL AX, r1+32(SP)
MOVL AX, r2+36(SP)
MOVL AX, err+40(SP)
RET
#define SYS_SEEK 39 /* from zsysnum_plan9_386.go */
//func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
TEXT ·seek(SB),7,$0
LEAL newoffset+24(SP), AX
MOVL AX, placeholder+4(SP)
MOVL $SYS_SEEK, AX // syscall entry
INT $64
CMPL AX, $-1
JNE ok6
MOVL AX, 24(SP) // newoffset low
MOVL AX, 28(SP) // newoffset high
SUBL $8, SP
CALL syscall·errstr(SB)
MOVL SP, SI
ADDL $8, SP
JMP copyresult6
ok6:
LEAL runtime·emptystring(SB), SI
copyresult6:
LEAL err+32(SP), DI
CLD
MOVSL
MOVSL
RET

View File

@ -0,0 +1,521 @@
// Copyright 2009 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.
// Fork, exec, wait, etc.
package syscall
import (
"sync"
"unsafe"
)
// Lock synchronizing creation of new file descriptors with fork.
//
// We want the child in a fork/exec sequence to inherit only the
// file descriptors we intend. To do that, we mark all file
// descriptors close-on-exec and then, in the child, explicitly
// unmark the ones we want the exec'ed program to keep.
// Unix doesn't make this easy: there is, in general, no way to
// allocate a new file descriptor close-on-exec. Instead you
// have to allocate the descriptor and then mark it close-on-exec.
// If a fork happens between those two events, the child's exec
// will inherit an unwanted file descriptor.
//
// This lock solves that race: the create new fd/mark close-on-exec
// operation is done holding ForkLock for reading, and the fork itself
// is done holding ForkLock for writing. At least, that's the idea.
// There are some complications.
//
// Some system calls that create new file descriptors can block
// for arbitrarily long times: open on a hung NFS server or named
// pipe, accept on a socket, and so on. We can't reasonably grab
// the lock across those operations.
//
// It is worse to inherit some file descriptors than others.
// If a non-malicious child accidentally inherits an open ordinary file,
// that's not a big deal. On the other hand, if a long-lived child
// accidentally inherits the write end of a pipe, then the reader
// of that pipe will not see EOF until that child exits, potentially
// causing the parent program to hang. This is a common problem
// in threaded C programs that use popen.
//
// Luckily, the file descriptors that are most important not to
// inherit are not the ones that can take an arbitrarily long time
// to create: pipe returns instantly, and the net package uses
// non-blocking I/O to accept on a listening socket.
// The rules for which file descriptor-creating operations use the
// ForkLock are as follows:
//
// 1) Pipe. Does not block. Use the ForkLock.
// 2) Socket. Does not block. Use the ForkLock.
// 3) Accept. If using non-blocking mode, use the ForkLock.
// Otherwise, live with the race.
// 4) Open. Can block. Use O_CLOEXEC if available (Linux).
// Otherwise, live with the race.
// 5) Dup. Does not block. Use the ForkLock.
// On Linux, could use fcntl F_DUPFD_CLOEXEC
// instead of the ForkLock, but only for dup(fd, -1).
var ForkLock sync.RWMutex
// Convert array of string to array
// of NUL-terminated byte pointer.
func StringArrayPtr(ss []string) []*byte {
bb := make([]*byte, len(ss)+1)
for i := 0; i < len(ss); i++ {
bb[i] = StringBytePtr(ss[i])
}
bb[len(ss)] = nil
return bb
}
// gbit16 reads a 16-bit numeric value from a 9P protocol message strored in b,
// returning the value and the remaining slice of b.
func gbit16(b []byte) (uint16, []byte) {
return uint16(b[0]) | uint16(b[1])<<8, b[2:]
}
// gstring reads a string from a 9P protocol message strored in b,
// returning the value as a Go string and the remaining slice of b.
func gstring(b []byte) (string, []byte) {
n, b := gbit16(b)
return string(b[0:n]), b[n:]
}
// readdirnames returns the names of files inside the directory represented by dirfd.
func readdirnames(dirfd int) (names []string, err Error) {
result := make([]string, 0, 100)
var buf [STATMAX]byte
for {
n, e := Read(dirfd, buf[:])
if e != nil {
return []string{}, e
}
if n == 0 {
break
}
for i := 0; i < n; {
m, _ := gbit16(buf[i:])
m += 2
if m < STATFIXLEN {
return []string{}, NewError("malformed stat buffer")
}
name, _ := gstring(buf[i+41:])
result = append(result, name)
i += int(m)
}
}
return []string{}, nil
}
// readdupdevice returns a list of currently opened fds (excluding stdin, stdout, stderr) from the dup device #d.
// ForkLock should be write locked before calling, so that no new fds would be created while the fd list is being read.
func readdupdevice() (fds []int, err Error) {
dupdevfd, err := Open("#d", O_RDONLY)
if err != nil {
return
}
defer Close(dupdevfd)
fileNames, err := readdirnames(dupdevfd)
if err != nil {
return
}
fds = make([]int, 0, len(fileNames)>>1)
for _, fdstr := range fileNames {
if l := len(fdstr); l > 2 && fdstr[l-3] == 'c' && fdstr[l-2] == 't' && fdstr[l-1] == 'l' {
continue
}
fd := int(atoi([]byte(fdstr)))
if fd == 0 || fd == 1 || fd == 2 || fd == dupdevfd {
continue
}
fds = append(fds, fd)
}
return fds[0:len(fds)], nil
}
var startupFds []int
// Plan 9 does not allow clearing the OCEXEC flag
// from the underlying channel backing an open file descriptor,
// therefore we store a list of already opened file descriptors
// inside startupFds and skip them when manually closing descriptors
// not meant to be passed to a child exec.
func init() {
startupFds, _ = readdupdevice()
}
// forkAndExecInChild forks the process, calling dup onto 0..len(fd)
// and finally invoking exec(argv0, argvv, envv) in the child.
// If a dup or exec fails, it writes the error string to pipe.
// (The pipe write end is close-on-exec so if exec succeeds, it will be closed.)
//
// In the child, this function must not acquire any locks, because
// they might have been locked at the time of the fork. This means
// no rescheduling, no malloc calls, and no new stack segments.
// The calls to RawSyscall are okay because they are assembly
// functions that do not grow the stack.
func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, chroot, dir *byte, attr *ProcAttr, fdsToClose []int, pipe int) (pid int, err Error) {
// Declare all variables at top in case any
// declarations require heap allocation (e.g., errbuf).
var (
r1 uintptr
nextfd int
i int
clearenv int
envfd int
errbuf [ERRMAX]byte
)
// guard against side effects of shuffling fds below.
fd := append([]int(nil), attr.Files...)
if envv != nil {
clearenv = RFCENVG
}
// About to call fork.
// No more allocation or calls of non-assembly functions.
r1, _, _ = RawSyscall(SYS_RFORK, uintptr(RFPROC|RFFDG|RFREND|clearenv), 0, 0)
if r1 != 0 {
if int(r1) == -1 {
return 0, NewError(errstr())
}
// parent; return PID
return int(r1), nil
}
// Fork succeeded, now in child.
// Close fds we don't need.
for i = 0; i < len(fdsToClose); i++ {
r1, _, _ = RawSyscall(SYS_CLOSE, uintptr(fdsToClose[i]), 0, 0)
if int(r1) == -1 {
goto childerror
}
}
if envv != nil {
// Write new environment variables.
for i = 0; i < len(envv); i++ {
r1, _, _ = RawSyscall(SYS_CREATE, uintptr(unsafe.Pointer(envv[i].name)), uintptr(O_WRONLY), uintptr(0666))
if int(r1) == -1 {
goto childerror
}
envfd = int(r1)
r1, _, _ = RawSyscall6(SYS_PWRITE, uintptr(envfd), uintptr(unsafe.Pointer(envv[i].value)), uintptr(envv[i].nvalue),
^uintptr(0), ^uintptr(0), 0)
if int(r1) == -1 || int(r1) != envv[i].nvalue {
goto childerror
}
r1, _, _ = RawSyscall(SYS_CLOSE, uintptr(envfd), 0, 0)
if int(r1) == -1 {
goto childerror
}
}
}
// Chdir
if dir != nil {
r1, _, _ = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
if int(r1) == -1 {
goto childerror
}
}
// Pass 1: look for fd[i] < i and move those up above len(fd)
// so that pass 2 won't stomp on an fd it needs later.
nextfd = int(len(fd))
if pipe < nextfd {
r1, _, _ = RawSyscall(SYS_DUP, uintptr(pipe), uintptr(nextfd), 0)
if int(r1) == -1 {
goto childerror
}
pipe = nextfd
nextfd++
}
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < int(i) {
r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(nextfd), 0)
if int(r1) == -1 {
goto childerror
}
fd[i] = nextfd
nextfd++
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
}
}
// Pass 2: dup fd[i] down onto i.
for i = 0; i < len(fd); i++ {
if fd[i] == -1 {
RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
continue
}
if fd[i] == int(i) {
continue
}
r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(i), 0)
if int(r1) == -1 {
goto childerror
}
RawSyscall(SYS_CLOSE, uintptr(fd[i]), 0, 0)
}
// Time to exec.
r1, _, _ = RawSyscall(SYS_EXEC,
uintptr(unsafe.Pointer(argv0)),
uintptr(unsafe.Pointer(&argv[0])), 0)
childerror:
// send error string on pipe
RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&errbuf[0])), uintptr(len(errbuf)), 0)
errbuf[len(errbuf)-1] = 0
i = 0
for i < len(errbuf) && errbuf[i] != 0 {
i++
}
RawSyscall6(SYS_PWRITE, uintptr(pipe), uintptr(unsafe.Pointer(&errbuf[0])), uintptr(i),
^uintptr(0), ^uintptr(0), 0)
for {
RawSyscall(SYS_EXITS, 0, 0, 0)
}
// Calling panic is not actually safe,
// but the for loop above won't break
// and this shuts up the compiler.
panic("unreached")
}
func cexecPipe(p []int) Error {
e := Pipe(p)
if e != nil {
return e
}
fd, e := Open("#d/"+itoa(p[1]), O_CLOEXEC)
if e != nil {
Close(p[0])
Close(p[1])
return e
}
Close(fd)
return nil
}
type envItem struct {
name *byte
value *byte
nvalue int
}
type ProcAttr struct {
Dir string // Current working directory.
Env []string // Environment.
Files []int // File descriptors.
Chroot string // Chroot.
}
var zeroAttributes ProcAttr
func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) {
var (
p [2]int
n int
errbuf [ERRMAX]byte
wmsg Waitmsg
)
if attr == nil {
attr = &zeroAttributes
}
p[0] = -1
p[1] = -1
// Convert args to C form.
argv0p := StringBytePtr(argv[0])
argvp := StringArrayPtr(argv)
var chroot *byte
if attr.Chroot != "" {
chroot = StringBytePtr(attr.Chroot)
}
var dir *byte
if attr.Dir != "" {
dir = StringBytePtr(attr.Dir)
}
var envvParsed []envItem
if attr.Env != nil {
envvParsed = make([]envItem, 0, len(attr.Env))
for _, v := range attr.Env {
i := 0
for i < len(v) && v[i] != '=' {
i++
}
envvParsed = append(envvParsed, envItem{StringBytePtr("/env/" + v[:i]), StringBytePtr(v[i+1:]), len(v) - i})
}
}
// Acquire the fork lock to prevent other threads from creating new fds before we fork.
ForkLock.Lock()
// get a list of open fds, excluding stdin,stdout and stderr that need to be closed in the child.
// no new fds can be created while we hold the ForkLock for writing.
openFds, e := readdupdevice()
if e != nil {
ForkLock.Unlock()
return 0, e
}
fdsToClose := make([]int, 0, len(openFds))
// exclude fds opened from startup from the list of fds to be closed.
for _, fd := range openFds {
isReserved := false
for _, reservedFd := range startupFds {
if fd == reservedFd {
isReserved = true
break
}
}
if !isReserved {
fdsToClose = append(fdsToClose, fd)
}
}
// exclude fds requested by the caller from the list of fds to be closed.
for _, fd := range openFds {
isReserved := false
for _, reservedFd := range attr.Files {
if fd == reservedFd {
isReserved = true
break
}
}
if !isReserved {
fdsToClose = append(fdsToClose, fd)
}
}
// Allocate child status pipe close on exec.
e = cexecPipe(p[:])
if e != nil {
return 0, e
}
fdsToClose = append(fdsToClose, p[0])
// Kick off child.
pid, err = forkAndExecInChild(argv0p, argvp, envvParsed, chroot, dir, attr, fdsToClose, p[1])
if err != nil {
if p[0] >= 0 {
Close(p[0])
Close(p[1])
}
ForkLock.Unlock()
return 0, err
}
ForkLock.Unlock()
// Read child error status from pipe.
Close(p[1])
n, err = Read(p[0], errbuf[:])
Close(p[0])
if err != nil || n != 0 {
if n != 0 {
err = NewError(string(errbuf[:]))
}
// Child failed; wait for it to exit, to make sure
// the zombies don't accumulate.
for wmsg.Pid != pid {
Await(&wmsg)
}
return 0, err
}
// Read got EOF, so pipe closed on exec, so exec succeeded.
return pid, nil
}
// Combination of fork and exec, careful to be thread safe.
func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) {
return forkExec(argv0, argv, attr)
}
// StartProcess wraps ForkExec for package os.
func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err Error) {
pid, err = forkExec(argv0, argv, attr)
return pid, 0, err
}
// Ordinary exec.
func Exec(argv0 string, argv []string, envv []string) (err Error) {
if envv != nil {
r1, _, _ := RawSyscall(SYS_RFORK, RFCENVG, 0, 0)
if int(r1) == -1 {
return NewError(errstr())
}
for _, v := range envv {
i := 0
for i < len(v) && v[i] != '=' {
i++
}
fd, e := Create("/env/"+v[:i], O_WRONLY, 0666)
if e != nil {
return e
}
_, e = Write(fd, []byte(v[i+1:]))
if e != nil {
Close(fd)
return e
}
Close(fd)
}
}
_, _, e := Syscall(SYS_EXEC,
uintptr(unsafe.Pointer(StringBytePtr(argv0))),
uintptr(unsafe.Pointer(&StringArrayPtr(argv)[0])),
0)
return NewError(e)
}

View File

@ -145,6 +145,12 @@ windows_386)
mktypes=
mkerrors="./mkerrors_windows.sh -f -m32"
;;
plan9_386)
mkerrors=
mksyscall="./mksyscall.pl -l32 -plan9"
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
mktypes="godefs -gsyscall -f -m32"
;;
*)
echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2
exit 1

View File

@ -23,6 +23,7 @@ $cmdline = "mksyscall.pl " . join(' ', @ARGV);
$errors = 0;
$_32bit = "";
$nacl = 0;
$plan9 = 0;
if($ARGV[0] eq "-b32") {
$_32bit = "big-endian";
@ -35,6 +36,10 @@ if($ARGV[0] eq "-nacl") {
$nacl = 1;
shift;
}
if($ARGV[0] eq "-plan9") {
$plan9 = 1;
shift;
}
if($ARGV[0] =~ /^-/) {
print STDERR "usage: mksyscall.pl [-b32 | -l32] [file ...]\n";
@ -160,9 +165,13 @@ while(<>) {
my $p = $out[$i];
my ($name, $type) = parseparam($p);
my $reg = "";
if($name eq "errno") {
if($name eq "errno" && !$plan9) {
$reg = "e1";
$ret[2] = $reg;
} elsif ($name eq "err" && $plan9) {
$ret[0] = "r0";
$ret[2] = "e1";
next;
} else {
$reg = sprintf("r%d", $i);
$ret[$i] = $reg;
@ -191,6 +200,13 @@ while(<>) {
$text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
}
$text .= $body;
if ($plan9 && $ret[2] eq "e1") {
$text .= "\terr = nil\n";
$text .= "\tif int(r0) == -1 {\n";
$text .= "\t\t err = NewError(e1)\n";
$text .= "\t}\n";
}
$text .= "\treturn\n";
$text .= "}\n\n";

View File

@ -0,0 +1,25 @@
#!/bin/sh
# Copyright 2009 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.# Copyright 2009 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.
COMMAND="mksysnum_plan9.sh $@"
cat <<EOF
// $COMMAND
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
const(
EOF
SP='[ ]' # space or tab
sed "s/^#define${SP}\\([A-Z0-9_][A-Z0-9_]*\\)${SP}${SP}*\\([0-9][0-9]*\\)/SYS_\\1=\\2/g" \
< $1 | grep -v SYS__
cat <<EOF
)
EOF

View File

@ -4,9 +4,9 @@
package syscall
func str(val int) string { // do it here rather than with fmt to avoid dependency
func itoa(val int) string { // do it here rather than with fmt to avoid dependency
if val < 0 {
return "-" + str(-val)
return "-" + itoa(-val)
}
var buf [32]byte // big enough for int64
i := len(buf) - 1

View File

@ -60,7 +60,7 @@ func Futimesat(dirfd int, path string, tv []Timeval) (errno int) {
func Futimes(fd int, tv []Timeval) (errno int) {
// Believe it or not, this is the best we can do on Linux
// (and is what glibc does).
return Utimes("/proc/self/fd/"+str(fd), tv)
return Utimes("/proc/self/fd/"+itoa(fd), tv)
}
const ImplementsGetwd = true

View File

@ -0,0 +1,343 @@
// Copyright 2011 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.
// Plan 9 system calls.
// This file is compiled as ordinary Go code,
// but it is also input to mksyscall,
// which parses the //sys lines and generates system call stubs.
// Note that sometimes we use a lowercase //sys name and
// wrap it in our own nicer implementation.
package syscall
import "unsafe"
const OS = "plan9"
const ImplementsGetwd = true
// An Error can represent any printable error condition.
type Error interface {
String() string
}
// ErrorString implements Error's String method by returning itself.
type ErrorString string
func (e ErrorString) String() string { return string(e) }
// NewError converts s to an ErrorString, which satisfies the Error interface.
func NewError(s string) Error { return ErrorString(s) }
var (
Stdin = 0
Stdout = 1
Stderr = 2
EISDIR Error = NewError("file is a directory")
)
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err string)
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err string)
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
func atoi(b []byte) (n uint) {
n = 0
for i := 0; i < len(b); i++ {
n = n*10 + uint(b[i]-'0')
}
return
}
func cstring(s []byte) string {
for i, _ := range s {
if s[i] == 0 {
return string(s[0:i])
}
}
return string(s)
}
func errstr() string {
var buf [ERRMAX]byte
RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
buf[len(buf)-1] = 0
return cstring(buf[:])
}
func Getpagesize() int { return 4096 }
//sys exits(msg *byte)
func Exits(msg *string) {
if msg == nil {
exits(nil)
}
exits(StringBytePtr(*msg))
}
func Exit(code int) {
if code == 0 {
Exits(nil)
}
msg := itoa(code)
Exits(&msg)
}
func readnum(path string) (uint, Error) {
var b [12]byte
fd, e := Open(path, O_RDONLY)
if e != nil {
return 0, e
}
defer Close(fd)
n, e := Pread(fd, b[:], 0)
if e != nil {
return 0, e
}
m := 0
for ; m < n && b[m] == ' '; m++ {
}
return atoi(b[m : n-1]), nil
}
func Getpid() (pid int) {
n, _ := readnum("#c/pid")
return int(n)
}
func Getppid() (ppid int) {
n, _ := readnum("#c/ppid")
return int(n)
}
func Read(fd int, p []byte) (n int, err Error) {
return Pread(fd, p, -1)
}
func Write(fd int, p []byte) (n int, err Error) {
return Pwrite(fd, p, -1)
}
func Getwd() (wd string, err Error) {
fd, e := Open(".", O_RDONLY)
if e != nil {
return "", e
}
defer Close(fd)
return Fd2path(fd)
}
//sys fd2path(fd int, buf []byte) (err Error)
func Fd2path(fd int) (path string, err Error) {
var buf [512]byte
e := fd2path(fd, buf[:])
if e != nil {
return "", e
}
return cstring(buf[:]), nil
}
//sys pipe(p *[2]_C_int) (err Error)
func Pipe(p []int) (err Error) {
if len(p) != 2 {
return NewError("bad arg in system call")
}
var pp [2]_C_int
err = pipe(&pp)
p[0] = int(pp[0])
p[1] = int(pp[1])
return
}
//sys sleep(millisecs int32) (err Error)
func Sleep(nsec int64) (err Error) {
return sleep(int32((nsec + 999) / 1e6)) // round up to microsecond
}
// Underlying system call writes to newoffset via pointer.
// Implemented in assembly to avoid allocation.
func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
func Seek(fd int, offset int64, whence int) (newoffset int64, err Error) {
newoffset, e := seek(0, fd, offset, whence)
err = nil
if newoffset == -1 {
err = NewError(e)
}
return
}
func Mkdir(path string, mode uint32) (err Error) {
fd, err := Create(path, O_RDONLY, DMDIR|mode)
if fd != -1 {
Close(fd)
}
return
}
type Waitmsg struct {
Pid int
Time [3]uint32
Msg string
}
//sys await(s []byte) (n int, err Error)
func Await(w *Waitmsg) (err Error) {
var buf [512]byte
var f [5][]byte
n, err := await(buf[:])
if err != nil || w == nil {
return
}
nf := 0
p := 0
for i := 0; i < n && nf < len(f)-1; i++ {
if buf[i] == ' ' {
f[nf] = buf[p:i]
p = i + 1
nf++
}
}
f[nf] = buf[p:]
nf++
if nf != len(f) {
return NewError("invalid wait message")
}
w.Pid = int(atoi(f[0]))
w.Time[0] = uint32(atoi(f[1]))
w.Time[1] = uint32(atoi(f[2]))
w.Time[2] = uint32(atoi(f[3]))
w.Msg = string(f[4])
return
}
func Unmount(name, old string) (err Error) {
oldp := uintptr(unsafe.Pointer(StringBytePtr(old)))
var r0 uintptr
var e string
// bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted.
if name == "" {
r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldp, 0)
} else {
r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(StringBytePtr(name))), oldp, 0)
}
err = nil
if int(r0) == -1 {
err = NewError(e)
}
return
}
func Fchdir(fd int) (err Error) {
path, err := Fd2path(fd)
if err != nil {
return
}
return Chdir(path)
}
type Timeval struct {
Sec int32
Usec int32
}
func NsecToTimeval(nsec int64) (tv Timeval) {
nsec += 999 // round up to microsecond
tv.Usec = int32(nsec % 1e9 / 1e3)
tv.Sec = int32(nsec / 1e9)
return
}
func DecodeBintime(b []byte) (nsec int64, err Error) {
if len(b) != 8 {
return -1, NewError("bad /dev/bintime format")
}
err = nil
nsec = int64(b[0])<<56 |
int64(b[1])<<48 |
int64(b[2])<<40 |
int64(b[3])<<32 |
int64(b[4])<<24 |
int64(b[5])<<16 |
int64(b[6])<<8 |
int64(b[7])
return
}
func Gettimeofday(tv *Timeval) (err Error) {
// TODO(paulzhol):
// avoid reopening a file descriptor for /dev/bintime on each call,
// use lower-level calls to avoid allocation.
var b [8]byte
var nsec int64
fd, e := Open("/dev/bintime", O_RDONLY)
if e != nil {
return e
}
defer Close(fd)
if _, e = Pread(fd, b[:], 0); e != nil {
return e
}
if nsec, e = DecodeBintime(b[:]); e != nil {
return e
}
*tv = NsecToTimeval(nsec)
return e
}
func Getegid() (egid int) { return -1 }
func Geteuid() (euid int) { return -1 }
func Getgid() (gid int) { return -1 }
func Getuid() (uid int) { return -1 }
func Getgroups() (gids []int, err Error) {
return make([]int, 0), nil
}
//sys Dup(oldfd int, newfd int) (fd int, err Error)
//sys Open(path string, mode int) (fd int, err Error)
//sys Create(path string, mode int, perm uint32) (fd int, err Error)
//sys Remove(path string) (err Error)
//sys Pread(fd int, p []byte, offset int64) (n int, err Error)
//sys Pwrite(fd int, p []byte, offset int64) (n int, err Error)
//sys Close(fd int) (err Error)
//sys Chdir(path string) (err Error)
//sys Bind(name string, old string, flag int) (err Error)
//sys Mount(fd int, afd int, old string, flag int, aname string) (err Error)
//sys Stat(path string, edir []byte) (n int, err Error)
//sys Fstat(fd int, edir []byte) (n int, err Error)
//sys Wstat(path string, edir []byte) (err Error)
//sys Fwstat(fd int, edir []byte) (err Error)

View File

@ -0,0 +1,5 @@
// Copyright 2009 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 syscall

View File

@ -17,7 +17,7 @@ func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
func Errstr(errno int) string {
if errno < 0 || errno >= int(len(errors)) {
return "error " + str(errno)
return "error " + itoa(errno)
}
return errors[errno]
}

View File

@ -175,7 +175,7 @@ func Errstr(errno int) string {
b := make([]uint16, 300)
n, err := FormatMessage(flags, 0, uint32(errno), 0, b, nil)
if err != 0 {
return "error " + str(errno) + " (FormatMessage failed with err=" + str(err) + ")"
return "error " + itoa(errno) + " (FormatMessage failed with err=" + itoa(err) + ")"
}
// trim terminating \r and \n
for ; n > 0 && (b[n-1] == '\n' || b[n-1] == '\r'); n-- {

View File

@ -0,0 +1,115 @@
// Copyright 2009 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.
/*
Input to godefs. See also mkerrors.sh and mkall.sh
*/
typedef unsigned short ushort;
typedef unsigned char uchar;
typedef unsigned long ulong;
typedef unsigned int uint;
typedef long long vlong;
typedef unsigned long long uvlong;
typedef int $_C_int;
enum {
OREAD = 0, // open for read
OWRITE = 1, // write
ORDWR = 2, // read and write
$O_RDONLY = OREAD,
$O_WRONLY = OWRITE,
$O_RDWR = ORDWR,
OEXEC = 3, // execute, == read but check execute permission
OTRUNC = 16, // or'ed in (except for exec), truncate file first
OCEXEC = 32, // or'ed in, close on exec
$O_CLOEXEC = OCEXEC,
ORCLOSE = 64, // or'ed in, remove on close
OEXCL = 0x1000, // or'ed in, exclusive use (create only)
$O_EXCL = OEXCL,
$STATMAX = 65535U,
$ERRMAX = 128,
$MORDER = 0x0003, // mask for bits defining order of mounting
$MREPL = 0x0000, // mount replaces object
$MBEFORE = 0x0001, // mount goes before others in union directory
$MAFTER = 0x0002, // mount goes after others in union directory
$MCREATE = 0x0004, // permit creation in mounted directory
$MCACHE = 0x0010, // cache some data
$MMASK = 0x0017, // all bits on
$RFNAMEG = (1<<0),
$RFENVG = (1<<1),
$RFFDG = (1<<2),
$RFNOTEG = (1<<3),
$RFPROC = (1<<4),
$RFMEM = (1<<5),
$RFNOWAIT = (1<<6),
$RFCNAMEG = (1<<10),
$RFCENVG = (1<<11),
$RFCFDG = (1<<12),
$RFREND = (1<<13),
$RFNOMNT = (1<<14),
// bits in Qid.type
$QTDIR = 0x80, // type bit for directories
$QTAPPEND = 0x40, // type bit for append only files
$QTEXCL = 0x20, // type bit for exclusive use files
$QTMOUNT = 0x10, // type bit for mounted channel
$QTAUTH = 0x08, // type bit for authentication file
$QTTMP = 0x04, // type bit for not-backed-up file
$QTFILE = 0x00, // plain file
// bits in Dir.mode
$DMDIR = 0x80000000, // mode bit for directories
$DMAPPEND = 0x40000000, // mode bit for append only files
$DMEXCL = 0x20000000, // mode bit for exclusive use files
$DMMOUNT = 0x10000000, // mode bit for mounted channel
$DMAUTH = 0x08000000, // mode bit for authentication file
$DMTMP = 0x04000000, // mode bit for non-backed-up files
$DMREAD = 0x4, // mode bit for read permission
$DMWRITE = 0x2, // mode bit for write permission
$DMEXEC = 0x1, // mode bit for execute permission
BIT8SZ = 1,
BIT16SZ = 2,
BIT32SZ = 4,
BIT64SZ = 8,
QIDSZ = BIT8SZ+BIT32SZ+BIT64SZ,
// STATFIXLEN includes leading 16-bit count
// The count, however, excludes itself; total size is BIT16SZ+count
$STATFIXLEN = BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+1*BIT64SZ, // amount of fixed length data in a stat buffer
};
struct Prof // Per process profiling
{
struct Plink *pp; // known to be 0(ptr)
struct Plink *next; // known to be 4(ptr)
struct Plink *last;
struct Plink *first;
ulong pid;
ulong what;
};
struct Tos {
struct Prof prof;
uvlong cyclefreq; // cycle clock frequency if there is one, 0 otherwise
vlong kcycles; // cycles spent in kernel
vlong pcycles; // cycles spent in process (kernel + user)
ulong pid; // might as well put the pid here
ulong clock;
// top of stack is here
};
typedef struct Prof $Prof;
typedef struct Tos $Tos;

View File

@ -0,0 +1,25 @@
package syscall
// Constants
const (
// Invented values to support what package os expects.
O_CREAT = 0x02000
O_NOCTTY = 0x00000
O_TRUNC = 0x00000
O_NONBLOCK = 0x00000
O_APPEND = 0x00000
O_SYNC = 0x00000
O_ASYNC = 0x00000
S_IFMT = 0x1f000
S_IFIFO = 0x1000
S_IFCHR = 0x2000
S_IFDIR = 0x4000
S_IFBLK = 0x6000
S_IFREG = 0x8000
S_IFLNK = 0xa000
S_IFSOCK = 0xc000
)
// Error table

View File

@ -0,0 +1,267 @@
// mksyscall.pl -l32 -plan9 syscall_plan9.go syscall_plan9_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
package syscall
import "unsafe"
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func exits(msg *byte) {
Syscall(SYS_EXITS, uintptr(unsafe.Pointer(msg)), 0, 0)
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func fd2path(fd int, buf []byte) (err Error) {
var _p0 unsafe.Pointer
if len(buf) > 0 {
_p0 = unsafe.Pointer(&buf[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func pipe(p *[2]_C_int) (err Error) {
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func sleep(millisecs int32) (err Error) {
r0, _, e1 := Syscall(SYS_SLEEP, uintptr(millisecs), 0, 0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func await(s []byte) (n int, err Error) {
var _p0 unsafe.Pointer
if len(s) > 0 {
_p0 = unsafe.Pointer(&s[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
n = int(r0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Dup(oldfd int, newfd int) (fd int, err Error) {
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
fd = int(r0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Open(path string, mode int) (fd int, err Error) {
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
fd = int(r0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Create(path string, mode int, perm uint32) (fd int, err Error) {
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), uintptr(perm))
fd = int(r0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Remove(path string) (err Error) {
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pread(fd int, p []byte, offset int64) (n int, err Error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
n = int(r0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Pwrite(fd int, p []byte, offset int64) (n int, err Error) {
var _p0 unsafe.Pointer
if len(p) > 0 {
_p0 = unsafe.Pointer(&p[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
n = int(r0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Close(fd int) (err Error) {
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Chdir(path string) (err Error) {
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(StringBytePtr(path))), 0, 0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Bind(name string, old string, flag int) (err Error) {
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(StringBytePtr(name))), uintptr(unsafe.Pointer(StringBytePtr(old))), uintptr(flag))
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Mount(fd int, afd int, old string, flag int, aname string) (err Error) {
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(StringBytePtr(old))), uintptr(flag), uintptr(unsafe.Pointer(StringBytePtr(aname))), 0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Stat(path string, edir []byte) (n int, err Error) {
var _p0 unsafe.Pointer
if len(edir) > 0 {
_p0 = unsafe.Pointer(&edir[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(edir)))
n = int(r0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fstat(fd int, edir []byte) (n int, err Error) {
var _p0 unsafe.Pointer
if len(edir) > 0 {
_p0 = unsafe.Pointer(&edir[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
n = int(r0)
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Wstat(path string, edir []byte) (err Error) {
var _p0 unsafe.Pointer
if len(edir) > 0 {
_p0 = unsafe.Pointer(&edir[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(_p0), uintptr(len(edir)))
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
func Fwstat(fd int, edir []byte) (err Error) {
var _p0 unsafe.Pointer
if len(edir) > 0 {
_p0 = unsafe.Pointer(&edir[0])
} else {
_p0 = unsafe.Pointer(&_zero)
}
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
err = nil
if int(r0) == -1 {
err = NewError(e1)
}
return
}

View File

@ -0,0 +1,47 @@
// mksysnum_plan9.sh /media/sys/src/libc/9syscall/sys.h
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
package syscall
const (
SYS_SYSR1 = 0
SYS_BIND = 2
SYS_CHDIR = 3
SYS_CLOSE = 4
SYS_DUP = 5
SYS_ALARM = 6
SYS_EXEC = 7
SYS_EXITS = 8
SYS_FAUTH = 10
SYS_SEGBRK = 12
SYS_OPEN = 14
SYS_OSEEK = 16
SYS_SLEEP = 17
SYS_RFORK = 19
SYS_PIPE = 21
SYS_CREATE = 22
SYS_FD2PATH = 23
SYS_BRK_ = 24
SYS_REMOVE = 25
SYS_NOTIFY = 28
SYS_NOTED = 29
SYS_SEGATTACH = 30
SYS_SEGDETACH = 31
SYS_SEGFREE = 32
SYS_SEGFLUSH = 33
SYS_RENDEZVOUS = 34
SYS_UNMOUNT = 35
SYS_SEMACQUIRE = 37
SYS_SEMRELEASE = 38
SYS_SEEK = 39
SYS_FVERSION = 40
SYS_ERRSTR = 41
SYS_STAT = 42
SYS_FSTAT = 43
SYS_WSTAT = 44
SYS_FWSTAT = 45
SYS_MOUNT = 46
SYS_AWAIT = 47
SYS_PREAD = 50
SYS_PWRITE = 51
)

View File

@ -0,0 +1,74 @@
// godefs -gsyscall -f -m32 types_plan9.c
// MACHINE GENERATED - DO NOT EDIT.
package syscall
// Constants
const (
O_RDONLY = 0
O_WRONLY = 0x1
O_RDWR = 0x2
O_CLOEXEC = 0x20
O_EXCL = 0x1000
STATMAX = 0xffff
ERRMAX = 0x80
MORDER = 0x3
MREPL = 0
MBEFORE = 0x1
MAFTER = 0x2
MCREATE = 0x4
MCACHE = 0x10
MMASK = 0x17
RFNAMEG = 0x1
RFENVG = 0x2
RFFDG = 0x4
RFNOTEG = 0x8
RFPROC = 0x10
RFMEM = 0x20
RFNOWAIT = 0x40
RFCNAMEG = 0x400
RFCENVG = 0x800
RFCFDG = 0x1000
RFREND = 0x2000
RFNOMNT = 0x4000
QTDIR = 0x80
QTAPPEND = 0x40
QTEXCL = 0x20
QTMOUNT = 0x10
QTAUTH = 0x8
QTTMP = 0x4
QTFILE = 0
DMDIR = 0x80000000
DMAPPEND = 0x40000000
DMEXCL = 0x20000000
DMMOUNT = 0x10000000
DMAUTH = 0x8000000
DMTMP = 0x4000000
DMREAD = 0x4
DMWRITE = 0x2
DMEXEC = 0x1
STATFIXLEN = 0x31
)
// Types
type _C_int int32
type Prof struct {
Pp *[0]byte /* sPlink */
Next *[0]byte /* sPlink */
Last *[0]byte /* sPlink */
First *[0]byte /* sPlink */
Pid uint32
What uint32
}
type Tos struct {
Prof Prof
Cyclefreq uint64
Kcycles int64
Pcycles int64
Pid uint32
Clock uint32
}