Merge pull request #533 from xushiwei/q

library os: StartProcess/Wait/Sysctl/environ; syscall.Wait4; c/syscall: zerrors; c/os: Setrlimit/Getrlimit;
This commit is contained in:
xushiwei
2024-07-18 22:52:39 +08:00
committed by GitHub
55 changed files with 50152 additions and 641 deletions

View File

@@ -215,6 +215,7 @@ The currently supported libraries include:
* [c/clang](https://pkg.go.dev/github.com/goplus/llgo/c/clang)
* [c/llama2](https://pkg.go.dev/github.com/goplus/llgo/c/llama2)
* [c/lua](https://pkg.go.dev/github.com/goplus/llgo/c/lua)
* [c/neco](https://pkg.go.dev/github.com/goplus/llgo/c/neco)
* [c/raylib](https://pkg.go.dev/github.com/goplus/llgo/c/raylib)
* [c/sqlite](https://pkg.go.dev/github.com/goplus/llgo/c/sqlite)
* [c/zlib](https://pkg.go.dev/github.com/goplus/llgo/c/zlib)

30
_cmptest/osproc/exec.go Normal file
View File

@@ -0,0 +1,30 @@
package main
import (
"fmt"
"os"
"os/exec"
"runtime"
)
func main() {
ls := "ls"
args := []string{ls, "-l"}
if runtime.GOOS == "windows" {
ls = "dir"
args = []string{ls}
}
lspath, _ := exec.LookPath(ls)
if lspath != "" {
ls = lspath
}
proc, err := os.StartProcess(ls, args, &os.ProcAttr{
Files: []*os.File{nil, os.Stdout, os.Stderr},
})
if err != nil {
fmt.Println("os.StartProcess error:", err)
return
}
proc.Wait()
fmt.Println("proc.Wait done")
}

View File

@@ -263,21 +263,50 @@ func Execve(path *c.Char, argv **c.Char, envp **c.Char) c.Int
//go:linkname Execvp C.execvp
func Execvp(file *c.Char, argv **c.Char) c.Int
// -----------------------------------------------------------------------------
type PidT c.Int
//go:linkname Fork C.fork
func Fork() c.Int
func Fork() PidT
//go:linkname Getpid C.getpid
func Getpid() PidT
//go:linkname Getppid C.getppid
func Getppid() PidT
//go:linkname Kill C.kill
func Kill(pid c.Int, sig c.Int) c.Int
func Kill(pid PidT, sig c.Int) c.Int
// If wait() returns due to a stopped or terminated child process, the process ID
// of the child is returned to the calling process. Otherwise, a value of -1 is
// returned and errno is set to indicate the error.
//
//go:linkname Wait C.wait
func Wait(statLoc *c.Int) PidT
// If wait3(), wait4(), or waitpid() returns due to a stopped or terminated child
// process, the process ID of the child is returned to the calling process. If
// there are no children not previously awaited, -1 is returned with errno set to
// [ECHILD]. Otherwise, if WNOHANG is specified and there are no stopped or exited
// children, 0 is returned. If an error is detected or a caught signal aborts the
// call, a value of -1 is returned and errno is set to indicate the error.
//
//go:linkname Wait3 C.wait3
func Wait3(statLoc *c.Int, options c.Int, rusage *syscall.Rusage) PidT
//go:linkname Wait4 C.wait4
func Wait4(pid PidT, statLoc *c.Int, options c.Int, rusage *syscall.Rusage) PidT
//go:linkname Waitpid C.waitpid
func Waitpid(pid PidT, statLoc *c.Int, options c.Int) PidT
// -----------------------------------------------------------------------------
//go:linkname Exit C.exit
func Exit(c.Int)
//go:linkname Getpid C.getpid
func Getpid() c.Int
//go:linkname Getppid C.getppid
func Getppid() c.Int
//go:linkname Getuid C.getuid
func Getuid() UidT
@@ -291,3 +320,43 @@ func Getgid() GidT
func Getegid() GidT
// -----------------------------------------------------------------------------
//go:linkname Getrlimit C.getrlimit
func Getrlimit(resource c.Int, rlp *syscall.Rlimit) c.Int
//go:linkname Setrlimit C.setrlimit
func Setrlimit(resource c.Int, rlp *syscall.Rlimit) c.Int
// -----------------------------------------------------------------------------
// Upon successful completion, the value 0 is returned; otherwise the value -1
// is returned and the global variable errno is set to indicate the error.
//
//go:linkname Sysctl C.sysctl
func Sysctl(
name *c.Int, namelen c.Uint,
oldp c.Pointer, oldlenp *uintptr,
newp c.Pointer, newlen uintptr) c.Int
//go:linkname Sysctlbyname C.sysctlbyname
func Sysctlbyname(
name *c.Char, oldp c.Pointer, oldlenp *uintptr,
newp c.Pointer, newlen uintptr) c.Int
// The sysctlnametomib() function accepts an ASCII representation of the
// name, looks up the integer name vector, and returns the numeric repre-
// sentation in the mib array pointed to by mibp. The number of elements
// in the mib array is given by the location specified by sizep before the
// call, and that location gives the number of entries copied after a suc-
// cessful call. The resulting mib and size may be used in subsequent
// sysctl() calls to get the data associated with the requested ASCII
// name. This interface is intended for use by applications that want to
// repeatedly request the same variable (the sysctl() function runs in
// about a third the time as the same request made via the sysctlbyname()
// function). The sysctlnametomib() function is also useful for fetching
// mib prefixes and then adding a final component.
//
//go:linkname Sysctlnametomib C.sysctlnametomib
func Sysctlnametomib(name *c.Char, mibp *c.Int, sizep *uintptr) c.Int
// -----------------------------------------------------------------------------

View File

@@ -19,3 +19,9 @@ package syscall
const (
LLGoPackage = "decl"
)
type Errno = uintptr
// A Signal is a number describing a process signal.
// It implements the os.Signal interface.
type Signal = int

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,148 @@
// mkerrors_windows.sh -m32
// Code generated by the command above; DO NOT EDIT.
package syscall
// Go names for Windows errors.
const (
ENOENT Errno = ERROR_FILE_NOT_FOUND
ENOTDIR Errno = ERROR_PATH_NOT_FOUND
)
// Windows reserves errors >= 1<<29 for application use.
const APPLICATION_ERROR = 1 << 29
// Invented values to support what package os and others expects.
const (
E2BIG Errno = APPLICATION_ERROR + iota
EACCES
EADDRINUSE
EADDRNOTAVAIL
EADV
EAFNOSUPPORT
EAGAIN
EALREADY
EBADE
EBADF
EBADFD
EBADMSG
EBADR
EBADRQC
EBADSLT
EBFONT
EBUSY
ECANCELED
ECHILD
ECHRNG
ECOMM
ECONNABORTED
ECONNREFUSED
ECONNRESET
EDEADLK
EDEADLOCK
EDESTADDRREQ
EDOM
EDOTDOT
EDQUOT
EEXIST
EFAULT
EFBIG
EHOSTDOWN
EHOSTUNREACH
EIDRM
EILSEQ
EINPROGRESS
EINTR
EINVAL
EIO
EISCONN
EISDIR
EISNAM
EKEYEXPIRED
EKEYREJECTED
EKEYREVOKED
EL2HLT
EL2NSYNC
EL3HLT
EL3RST
ELIBACC
ELIBBAD
ELIBEXEC
ELIBMAX
ELIBSCN
ELNRNG
ELOOP
EMEDIUMTYPE
EMFILE
EMLINK
EMSGSIZE
EMULTIHOP
ENAMETOOLONG
ENAVAIL
ENETDOWN
ENETRESET
ENETUNREACH
ENFILE
ENOANO
ENOBUFS
ENOCSI
ENODATA
ENODEV
ENOEXEC
ENOKEY
ENOLCK
ENOLINK
ENOMEDIUM
ENOMEM
ENOMSG
ENONET
ENOPKG
ENOPROTOOPT
ENOSPC
ENOSR
ENOSTR
ENOSYS
ENOTBLK
ENOTCONN
ENOTEMPTY
ENOTNAM
ENOTRECOVERABLE
ENOTSOCK
ENOTSUP
ENOTTY
ENOTUNIQ
ENXIO
EOPNOTSUPP
EOVERFLOW
EOWNERDEAD
EPERM
EPFNOSUPPORT
EPIPE
EPROTO
EPROTONOSUPPORT
EPROTOTYPE
ERANGE
EREMCHG
EREMOTE
EREMOTEIO
ERESTART
EROFS
ESHUTDOWN
ESOCKTNOSUPPORT
ESPIPE
ESRCH
ESRMNT
ESTALE
ESTRPIPE
ETIME
ETIMEDOUT
ETOOMANYREFS
ETXTBSY
EUCLEAN
EUNATCH
EUSERS
EWOULDBLOCK
EXDEV
EXFULL
EWINDOWS
)

161
internal/lib/os/env.go Normal file
View File

@@ -0,0 +1,161 @@
// Copyright 2010 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.
// General environment variables.
package os
import (
"syscall"
)
/* TODO(xsw):
// Expand replaces ${var} or $var in the string based on the mapping function.
// For example, os.ExpandEnv(s) is equivalent to os.Expand(s, os.Getenv).
func Expand(s string, mapping func(string) string) string {
var buf []byte
// ${} is all ASCII, so bytes are fine for this operation.
i := 0
for j := 0; j < len(s); j++ {
if s[j] == '$' && j+1 < len(s) {
if buf == nil {
buf = make([]byte, 0, 2*len(s))
}
buf = append(buf, s[i:j]...)
name, w := getShellName(s[j+1:])
if name == "" && w > 0 {
// Encountered invalid syntax; eat the
// characters.
} else if name == "" {
// Valid syntax, but $ was not followed by a
// name. Leave the dollar character untouched.
buf = append(buf, s[j])
} else {
buf = append(buf, mapping(name)...)
}
j += w
i = j + 1
}
}
if buf == nil {
return s
}
return string(buf) + s[i:]
}
// ExpandEnv replaces ${var} or $var in the string according to the values
// of the current environment variables. References to undefined
// variables are replaced by the empty string.
func ExpandEnv(s string) string {
return Expand(s, Getenv)
}
// isShellSpecialVar reports whether the character identifies a special
// shell variable such as $*.
func isShellSpecialVar(c uint8) bool {
switch c {
case '*', '#', '$', '@', '!', '?', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return true
}
return false
}
// isAlphaNum reports whether the byte is an ASCII letter, number, or underscore.
func isAlphaNum(c uint8) bool {
return c == '_' || '0' <= c && c <= '9' || 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
}
// getShellName returns the name that begins the string and the number of bytes
// consumed to extract it. If the name is enclosed in {}, it's part of a ${}
// expansion and two more bytes are needed than the length of the name.
func getShellName(s string) (string, int) {
switch {
case s[0] == '{':
if len(s) > 2 && isShellSpecialVar(s[1]) && s[2] == '}' {
return s[1:2], 3
}
// Scan to closing brace
for i := 1; i < len(s); i++ {
if s[i] == '}' {
if i == 1 {
return "", 2 // Bad syntax; eat "${}"
}
return s[1:i], i + 1
}
}
return "", 1 // Bad syntax; eat "${"
case isShellSpecialVar(s[0]):
return s[0:1], 1
}
// Scan alphanumerics.
var i int
for i = 0; i < len(s) && isAlphaNum(s[i]); i++ {
}
return s[:i], i
}
*/
// Getenv retrieves the value of the environment variable named by the key.
// It returns the value, which will be empty if the variable is not present.
// To distinguish between an empty value and an unset value, use LookupEnv.
func Getenv(key string) string {
v, _ := syscall.Getenv(key)
return v
}
/* TODO(xsw):
func Getenv(key string) string {
return c.GoString(os.Getenv(c.AllocaCStr(key)))
}
*/
// LookupEnv retrieves the value of the environment variable named
// by the key. If the variable is present in the environment the
// value (which may be empty) is returned and the boolean is true.
// Otherwise the returned value will be empty and the boolean will
// be false.
func LookupEnv(key string) (string, bool) {
return syscall.Getenv(key)
}
// Setenv sets the value of the environment variable named by the key.
// It returns an error, if any.
func Setenv(key, value string) error {
err := syscall.Setenv(key, value)
if err != nil {
return NewSyscallError("setenv", err)
}
return nil
}
/* TODO(xsw):
func Setenv(key, value string) error {
ret := os.Setenv(c.AllocaCStr(key), c.AllocaCStr(value), 1)
if ret == 0 {
return nil
}
return &SyscallError{"setenv", syscall.Errno(ret)}
}
*/
// Unsetenv unsets a single environment variable.
func Unsetenv(key string) error {
return syscall.Unsetenv(key)
}
/* TODO(xsw):
func Unsetenv(key string) error {
ret := os.Unsetenv(c.AllocaCStr(key))
if ret == 0 {
return nil
}
return syscall.Errno(ret)
}
*/
// Environ returns a copy of strings representing the environment,
// in the form "key=value".
func Environ() []string {
return syscall.Environ()
}

View File

@@ -14,7 +14,6 @@ import (
)
func (p *Process) wait() (ps *ProcessState, err error) {
/* TODO(xsw):
if p.Pid == -1 {
return nil, syscall.EINVAL
}
@@ -58,8 +57,6 @@ func (p *Process) wait() (ps *ProcessState, err error) {
rusage: &rusage,
}
return ps, nil
*/
panic("todo: os.Process.wait")
}
func (p *Process) signal(sig Signal) error {

View File

@@ -148,10 +148,6 @@ func Clearenv()
// TODO(xsw):
// func DirFS(dir string) fs.FS
func Environ() []string {
panic("todo: os.Environ")
}
// func Executable() (string, error)
// TODO(xsw):
@@ -162,10 +158,6 @@ func Getegid() int {
return int(os.Getegid())
}
func Getenv(key string) string {
return c.GoString(os.Getenv(c.AllocaCStr(key)))
}
func Geteuid() int {
return int(os.Geteuid())
}
@@ -330,14 +322,6 @@ func Rename(oldpath, newpath string) error {
// TODO(xsw):
// func SameFile(fi1, fi2 FileInfo) bool
func Setenv(key, value string) error {
ret := os.Setenv(c.AllocaCStr(key), c.AllocaCStr(value), 1)
if ret == 0 {
return nil
}
return &SyscallError{"setenv", syscall.Errno(ret)}
}
func Symlink(oldname, newname string) error {
ret := os.Symlink(c.AllocaCStr(oldname), c.AllocaCStr(newname))
if ret == 0 {
@@ -357,14 +341,6 @@ func Truncate(name string, size int64) error {
return toPathErr("truncate", name, ret)
}
func Unsetenv(key string) error {
ret := os.Unsetenv(c.AllocaCStr(key))
if ret == 0 {
return nil
}
return syscall.Errno(ret)
}
// UserCacheDir returns the default root directory to use for user-specific
// cached data. Users should create their own application-specific subdirectory
// within this one and use that.

View File

@@ -0,0 +1,21 @@
// Copyright 2016 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.
// aix, darwin, js/wasm, openbsd, solaris and wasip1/wasm don't implement
// waitid/wait6.
//go:build aix || darwin || (js && wasm) || openbsd || solaris || wasip1
package os
// blockUntilWaitable attempts to block until a call to p.Wait will
// succeed immediately, and reports whether it has done so.
// It does not actually call p.Wait.
// This version is used on systems that do not implement waitid,
// or where we have not implemented it yet. Note that this is racy:
// a call to Process.Signal can in an extremely unlikely case send a
// signal to the wrong process, see issue #13987.
func (p *Process) blockUntilWaitable() (bool, error) {
return false, nil
}

View File

@@ -7,5 +7,6 @@
package runtime
func SetFinalizer(obj any, finalizer any) {
panic("todo: runtime.SetFinalizer")
// TODO(xsw):
// panic("todo: runtime.SetFinalizer")
}

View File

@@ -0,0 +1,174 @@
// Copyright 2010 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.
//go:build unix || (js && wasm) || plan9 || wasip1
// Unix environment variables.
package syscall
import (
"runtime"
"sync"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
var (
// envOnce guards initialization by copyenv, which populates env.
envOnce sync.Once
// envLock guards env and envs.
envLock sync.RWMutex
// env maps from an environment variable to its first occurrence in envs.
env map[string]int
// envs is provided by the runtime. elements are expected to
// be of the form "key=value". An empty string means deleted
// (or a duplicate to be ignored).
envs []string = runtimeEnvs()
)
func runtimeEnvs() []string {
ret := make([]string, 0, 8)
cenvs := os.Environ
i := 0
for {
ce := *c.Advance(cenvs, i)
if ce == nil {
return ret
}
ret = append(ret, c.GoString(ce))
i++
}
}
func copyenv() {
env = make(map[string]int)
for i, s := range envs {
for j := 0; j < len(s); j++ {
if s[j] == '=' {
key := s[:j]
if _, ok := env[key]; !ok {
env[key] = i // first mention of key
} else {
// Clear duplicate keys. This permits Unsetenv to
// safely delete only the first item without
// worrying about unshadowing a later one,
// which might be a security problem.
envs[i] = ""
}
break
}
}
}
}
func Unsetenv(key string) error {
envOnce.Do(copyenv)
envLock.Lock()
defer envLock.Unlock()
if i, ok := env[key]; ok {
envs[i] = ""
delete(env, key)
}
os.Unsetenv(c.AllocaCStr(key))
return nil
}
func Getenv(key string) (value string, found bool) {
envOnce.Do(copyenv)
if len(key) == 0 {
return "", false
}
envLock.RLock()
defer envLock.RUnlock()
i, ok := env[key]
if !ok {
return "", false
}
s := envs[i]
for i := 0; i < len(s); i++ {
if s[i] == '=' {
return s[i+1:], true
}
}
return "", false
}
/* TODO(xsw):
func Getenv(key string) (value string, found bool) {
ret := os.Getenv(c.AllocaCStr(key))
if ret != nil {
return c.GoString(ret), true
}
return "", false
}
*/
func Setenv(key, value string) error {
envOnce.Do(copyenv)
if len(key) == 0 {
return Errno(syscall.EINVAL)
}
for i := 0; i < len(key); i++ {
if key[i] == '=' || key[i] == 0 {
return Errno(syscall.EINVAL)
}
}
// On Plan 9, null is used as a separator, eg in $path.
if runtime.GOOS != "plan9" {
for i := 0; i < len(value); i++ {
if value[i] == 0 {
return Errno(syscall.EINVAL)
}
}
}
envLock.Lock()
defer envLock.Unlock()
i, ok := env[key]
kv := key + "=" + value
if ok {
envs[i] = kv
} else {
i = len(envs)
envs = append(envs, kv)
}
env[key] = i
os.Setenv(c.AllocaCStr(key), c.AllocaCStr(value), 1)
return nil
}
func Clearenv() {
envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv
envLock.Lock()
defer envLock.Unlock()
os.Clearenv()
env = make(map[string]int)
envs = []string{}
}
func Environ() []string {
envOnce.Do(copyenv)
envLock.RLock()
defer envLock.RUnlock()
a := make([]string, 0, len(envs))
for _, env := range envs {
if env != "" {
a = append(a, env)
}
}
return a
}

View File

@@ -0,0 +1,96 @@
// Copyright 2010 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.
// Windows environment variables.
package syscall
import (
"unsafe"
)
func Getenv(key string) (value string, found bool) {
keyp, err := UTF16PtrFromString(key)
if err != nil {
return "", false
}
n := uint32(100)
for {
b := make([]uint16, n)
n, err = GetEnvironmentVariable(keyp, &b[0], uint32(len(b)))
if n == 0 && err == ERROR_ENVVAR_NOT_FOUND {
return "", false
}
if n <= uint32(len(b)) {
return UTF16ToString(b[:n]), true
}
}
}
func Setenv(key, value string) error {
v, err := UTF16PtrFromString(value)
if err != nil {
return err
}
keyp, err := UTF16PtrFromString(key)
if err != nil {
return err
}
e := SetEnvironmentVariable(keyp, v)
if e != nil {
return e
}
runtimeSetenv(key, value)
return nil
}
func Unsetenv(key string) error {
keyp, err := UTF16PtrFromString(key)
if err != nil {
return err
}
e := SetEnvironmentVariable(keyp, nil)
if e != nil {
return e
}
runtimeUnsetenv(key)
return nil
}
func Clearenv() {
for _, s := range Environ() {
// Environment variables can begin with =
// so start looking for the separator = at j=1.
// https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
for j := 1; j < len(s); j++ {
if s[j] == '=' {
Unsetenv(s[0:j])
break
}
}
}
}
func Environ() []string {
envp, e := GetEnvironmentStrings()
if e != nil {
return nil
}
defer FreeEnvironmentStrings(envp)
r := make([]string, 0, 50) // Empty with room to grow.
const size = unsafe.Sizeof(*envp)
for *envp != 0 { // environment block ends with empty string
// find NUL terminator
end := unsafe.Pointer(envp)
for *(*uint16)(end) != 0 {
end = unsafe.Add(end, size)
}
entry := unsafe.Slice(envp, (uintptr(end)-uintptr(unsafe.Pointer(envp)))/size)
r = append(r, UTF16ToString(entry))
envp = (*uint16)(unsafe.Add(end, size))
}
return r
}

View File

@@ -6,6 +6,12 @@
package syscall
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
type SysProcAttr struct {
Chroot string // Chroot.
Credential *Credential // Credential.
@@ -48,146 +54,191 @@ func runtime_AfterForkInChild()
// functions that do not grow the stack.
//
//go:norace
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err1 Errno) {
/* TODO(xsw):
// Declare all variables at top in case any
// declarations require heap allocation (e.g., err1).
var (
r1 uintptr
nextfd int
i int
err error
pgrp _C_int
cred *Credential
ngroups, groups uintptr
)
func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err1 Errno) {
// Declare all variables at top in case any
// declarations require heap allocation (e.g., err1).
var (
r1 uintptr
nextfd int
i int
cred *Credential
// err error
// pgrp c.Int
// ngroups, groups uintptr
)
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
// guard against side effects of shuffling fds below.
// Make sure that nextfd is beyond any currently open files so
// that we can't run the risk of overwriting any of them.
fd := make([]int, len(attr.Files))
nextfd = len(attr.Files)
for i, ufd := range attr.Files {
if nextfd < int(ufd) {
nextfd = int(ufd)
}
fd[i] = int(ufd)
// guard against side effects of shuffling fds below.
// Make sure that nextfd is beyond any currently open files so
// that we can't run the risk of overwriting any of them.
fd := make([]int, len(attr.Files))
nextfd = len(attr.Files)
for i, ufd := range attr.Files {
if nextfd < int(ufd) {
nextfd = int(ufd)
}
nextfd++
fd[i] = int(ufd)
}
nextfd++
// About to call fork.
// No more allocation or calls of non-assembly functions.
runtime_BeforeFork()
r1, _, err1 = rawSyscall(abi.FuncPCABI0(libc_fork_trampoline), 0, 0, 0)
// About to call fork.
// No more allocation or calls of non-assembly functions.
// runtime_BeforeFork()
r1, err1 = fork()
if err1 != 0 {
// runtime_AfterFork()
return 0, err1
}
if r1 != 0 {
// parent; return PID
// runtime_AfterFork()
return int(r1), 0
}
// Fork succeeded, now in child.
// TODO(xsw): check this
// Enable tracing if requested.
if sys.Ptrace {
/* TODO(xsw):
if err = ptrace(PTRACE_TRACEME, 0, 0, 0); err != nil {
err1 = err.(Errno)
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Ptrace")
}
// Session ID
if sys.Setsid {
/* TODO(xsw):
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setsid_trampoline), 0, 0, 0)
if err1 != 0 {
runtime_AfterFork()
return 0, err1
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Setsid")
}
if r1 != 0 {
// parent; return PID
runtime_AfterFork()
return int(r1), 0
// Set process group
if sys.Setpgid || sys.Foreground {
/* TODO(xsw):
// Place child in process group.
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setpgid_trampoline), 0, uintptr(sys.Pgid), 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Setpgid")
}
// Fork succeeded, now in child.
// Enable tracing if requested.
if sys.Ptrace {
if err = ptrace(PTRACE_TRACEME, 0, 0, 0); err != nil {
err1 = err.(Errno)
if sys.Foreground {
/* TODO(xsw):
// This should really be pid_t, however _C_int (aka int32) is
// generally equivalent.
pgrp = _C_int(sys.Pgid)
if pgrp == 0 {
r1, _, err1 = rawSyscall(abi.FuncPCABI0(libc_getpid_trampoline), 0, 0, 0)
if err1 != 0 {
goto childerror
}
pgrp = _C_int(r1)
}
// Session ID
if sys.Setsid {
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setsid_trampoline), 0, 0, 0)
// Place process group in foreground.
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Foreground")
}
// Restore the signal mask. We do this after TIOCSPGRP to avoid
// having the kernel send a SIGTTOU signal to the process group.
// runtime_AfterForkInChild()
// Chroot
if chroot != nil {
/* TODO(xsw):
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_chroot_trampoline), uintptr(unsafe.Pointer(chroot)), 0, 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - chroot")
}
// User and groups
if cred = sys.Credential; cred != nil {
/* TODO(xsw):
ngroups = uintptr(len(cred.Groups))
groups = uintptr(0)
if ngroups > 0 {
groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
}
if !cred.NoSetGroups {
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setgroups_trampoline), ngroups, groups, 0)
if err1 != 0 {
goto childerror
}
}
// Set process group
if sys.Setpgid || sys.Foreground {
// Place child in process group.
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setpgid_trampoline), 0, uintptr(sys.Pgid), 0)
if err1 != 0 {
goto childerror
}
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setgid_trampoline), uintptr(cred.Gid), 0, 0)
if err1 != 0 {
goto childerror
}
if sys.Foreground {
// This should really be pid_t, however _C_int (aka int32) is
// generally equivalent.
pgrp = _C_int(sys.Pgid)
if pgrp == 0 {
r1, _, err1 = rawSyscall(abi.FuncPCABI0(libc_getpid_trampoline), 0, 0, 0)
if err1 != 0 {
goto childerror
}
pgrp = _C_int(r1)
}
// Place process group in foreground.
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
if err1 != 0 {
goto childerror
}
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setuid_trampoline), uintptr(cred.Uid), 0, 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Credential")
}
// Restore the signal mask. We do this after TIOCSPGRP to avoid
// having the kernel send a SIGTTOU signal to the process group.
runtime_AfterForkInChild()
// Chroot
if chroot != nil {
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_chroot_trampoline), uintptr(unsafe.Pointer(chroot)), 0, 0)
if err1 != 0 {
goto childerror
}
// Chdir
if dir != nil {
/* TODO(xsw):
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_chdir_trampoline), uintptr(unsafe.Pointer(dir)), 0, 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - dir")
}
// User and groups
if cred = sys.Credential; cred != nil {
ngroups = uintptr(len(cred.Groups))
groups = uintptr(0)
if ngroups > 0 {
groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
}
if !cred.NoSetGroups {
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setgroups_trampoline), ngroups, groups, 0)
if err1 != 0 {
goto childerror
}
}
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setgid_trampoline), uintptr(cred.Gid), 0, 0)
if err1 != 0 {
goto childerror
}
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setuid_trampoline), uintptr(cred.Uid), 0, 0)
// 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.
if pipe < nextfd {
/* TODO(xsw):
if runtime.GOOS == "openbsd" {
_, _, err1 = rawSyscall(dupTrampoline, uintptr(pipe), uintptr(nextfd), O_CLOEXEC)
} else {
_, _, err1 = rawSyscall(dupTrampoline, uintptr(pipe), uintptr(nextfd), 0)
if err1 != 0 {
goto childerror
}
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_fcntl_trampoline), uintptr(nextfd), F_SETFD, FD_CLOEXEC)
}
// Chdir
if dir != nil {
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_chdir_trampoline), uintptr(unsafe.Pointer(dir)), 0, 0)
if err1 != 0 {
goto childerror
}
if err1 != 0 {
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.
if pipe < nextfd {
pipe = nextfd
nextfd++
*/
panic("todo: syscall.forkAndExecInChild - pipe < nextfd")
}
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < i {
/* TODO(xsw):
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
if runtime.GOOS == "openbsd" {
_, _, err1 = rawSyscall(dupTrampoline, uintptr(pipe), uintptr(nextfd), O_CLOEXEC)
_, _, err1 = rawSyscall(dupTrampoline, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC)
} else {
_, _, err1 = rawSyscall(dupTrampoline, uintptr(pipe), uintptr(nextfd), 0)
_, _, err1 = rawSyscall(dupTrampoline, uintptr(fd[i]), uintptr(nextfd), 0)
if err1 != 0 {
goto childerror
}
@@ -196,95 +247,94 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
if err1 != 0 {
goto childerror
}
pipe = nextfd
fd[i] = nextfd
nextfd++
*/
panic("todo: syscall.forkAndExecInChild - for fd")
}
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < i {
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
if runtime.GOOS == "openbsd" {
_, _, err1 = rawSyscall(dupTrampoline, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC)
} else {
_, _, err1 = rawSyscall(dupTrampoline, uintptr(fd[i]), uintptr(nextfd), 0)
if err1 != 0 {
goto childerror
}
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_fcntl_trampoline), uintptr(nextfd), F_SETFD, FD_CLOEXEC)
}
if err1 != 0 {
goto childerror
}
fd[i] = nextfd
nextfd++
}
}
}
// Pass 2: dup fd[i] down onto i.
for i = 0; i < len(fd); i++ {
if fd[i] == -1 {
rawSyscall(abi.FuncPCABI0(libc_close_trampoline), uintptr(i), 0, 0)
continue
}
if fd[i] == i {
// dup2(i, i) won't clear close-on-exec flag on Linux,
// probably not elsewhere either.
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_fcntl_trampoline), uintptr(fd[i]), F_SETFD, 0)
if err1 != 0 {
goto childerror
}
continue
}
// The new fd is created NOT close-on-exec,
// which is exactly what we want.
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_dup2_trampoline), uintptr(fd[i]), uintptr(i), 0)
if err1 != 0 {
// Pass 2: dup fd[i] down onto i.
for i = 0; i < len(fd); i++ {
if fd[i] == -1 {
os.Close(c.Int(i))
continue
}
if fd[i] == i {
// dup2(i, i) won't clear close-on-exec flag on Linux,
// probably not elsewhere either.
ret := os.Fcntl(c.Int(fd[i]), syscall.F_SETFD, 0)
if ret != 0 {
err1 = Errno(ret)
goto childerror
}
continue
}
// By convention, we don't close-on-exec the fds we are
// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
// Programs that know they inherit fds >= 3 will need
// to set them close-on-exec.
for i = len(fd); i < 3; i++ {
rawSyscall(abi.FuncPCABI0(libc_close_trampoline), uintptr(i), 0, 0)
/* TODO(xsw):
// The new fd is created NOT close-on-exec,
// which is exactly what we want.
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_dup2_trampoline), uintptr(fd[i]), uintptr(i), 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - dup2")
}
// Detach fd 0 from tty
if sys.Noctty {
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), 0, uintptr(TIOCNOTTY), 0)
if err1 != 0 {
goto childerror
}
// By convention, we don't close-on-exec the fds we are
// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
// Programs that know they inherit fds >= 3 will need
// to set them close-on-exec.
for i = len(fd); i < 3; i++ {
/* TODO(xsw):
rawSyscall(abi.FuncPCABI0(libc_close_trampoline), uintptr(i), 0, 0)
*/
panic("todo: syscall.forkAndExecInChild - for i")
}
// Detach fd 0 from tty
if sys.Noctty {
/* TODO(xsw):
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), 0, uintptr(TIOCNOTTY), 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Noctty")
}
// Set the controlling TTY to Ctty
if sys.Setctty {
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
if err1 != 0 {
goto childerror
}
// Set the controlling TTY to Ctty
if sys.Setctty {
/* TODO(xsw):
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Setctty")
}
// Restore original rlimit.
if rlimOK && rlim.Cur != 0 {
rawSyscall(abi.FuncPCABI0(libc_setrlimit_trampoline), uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(&rlim)), 0)
}
// Restore original rlimit.
if rlimOK && rlim.Cur != 0 {
os.Setrlimit(syscall.RLIMIT_NOFILE, (*syscall.Rlimit)(&rlim))
}
// Time to exec.
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_execve_trampoline),
uintptr(unsafe.Pointer(argv0)),
uintptr(unsafe.Pointer(&argv[0])),
uintptr(unsafe.Pointer(&envv[0])))
childerror:
// send error code on pipe
rawSyscall(abi.FuncPCABI0(libc_write_trampoline), uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
for {
rawSyscall(abi.FuncPCABI0(libc_exit_trampoline), 253, 0, 0)
}
// Time to exec.
os.Execve(argv0, argv, envv)
/* TODO(xsw):
ret := os.Execve(argv0, argv, envv)
if ret != 0 {
err1 = Errno(ret)
}
*/
panic("todo: syscall.forkAndExecInChild")
childerror:
/* TODO(xsw):
// send error code on pipe
rawSyscall(abi.FuncPCABI0(libc_write_trampoline), uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
for {
rawSyscall(abi.FuncPCABI0(libc_exit_trampoline), 253, 0, 0)
}
*/
panic("todo: syscall.forkAndExecInChild - childerror")
}

View File

@@ -9,11 +9,14 @@
package syscall
import (
"errors"
"runtime"
"sync"
"syscall"
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
// ForkLock is used to synchronize creation of new file descriptors
@@ -106,13 +109,10 @@ type ProcAttr struct {
Sys *SysProcAttr
}
/* TODO(xsw):
var zeroProcAttr ProcAttr
var zeroSysProcAttr SysProcAttr
*/
func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
/* TODO(xsw):
var p [2]int
var n int
var err1 Errno
@@ -127,45 +127,30 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
}
// Convert args to C form.
argv0p, err := BytePtrFromString(argv0)
if err != nil {
return 0, err
}
argvp, err := SlicePtrFromStrings(argv)
if err != nil {
return 0, err
}
envvp, err := SlicePtrFromStrings(attr.Env)
if err != nil {
return 0, err
}
argv0p := c.AllocaCStr(argv0)
argvp := c.AllocaCStrs(argv, true)
envvp := c.AllocaCStrs(attr.Env, true)
if (runtime.GOOS == "freebsd" || runtime.GOOS == "dragonfly") && len(argv) > 0 && len(argv[0]) > len(argv0) {
argvp[0] = argv0p
*argvp = argv0p
}
var chroot *byte
var chroot *c.Char
if sys.Chroot != "" {
chroot, err = BytePtrFromString(sys.Chroot)
if err != nil {
return 0, err
}
chroot = c.AllocaCStr(sys.Chroot)
}
var dir *byte
var dir *c.Char
if attr.Dir != "" {
dir, err = BytePtrFromString(attr.Dir)
if err != nil {
return 0, err
}
dir = c.AllocaCStr(attr.Dir)
}
// Both Setctty and Foreground use the Ctty field,
// but they give it slightly different meanings.
if sys.Setctty && sys.Foreground {
return 0, errorspkg.New("both Setctty and Foreground set in SysProcAttr")
return 0, errors.New("both Setctty and Foreground set in SysProcAttr")
}
if sys.Setctty && sys.Ctty >= len(attr.Files) {
return 0, errorspkg.New("Setctty set but Ctty not valid in child")
return 0, errors.New("Setctty set but Ctty not valid in child")
}
acquireForkLock()
@@ -190,7 +175,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
Close(p[1])
for {
n, err = readlen(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1)))
if err != EINTR {
if err != Errno(syscall.EINTR) {
break
}
}
@@ -200,13 +185,13 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
err = Errno(err1)
}
if err == nil {
err = EPIPE
err = Errno(syscall.EPIPE)
}
// Child failed; wait for it to exit, to make sure
// the zombies don't accumulate.
_, err1 := Wait4(pid, &wstatus, 0, nil)
for err1 == EINTR {
for err1 == Errno(syscall.EINTR) {
_, err1 = Wait4(pid, &wstatus, 0, nil)
}
return 0, err
@@ -214,8 +199,6 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
// Read got EOF, so pipe closed on exec, so exec succeeded.
return pid, nil
*/
panic("todo: syscall.forkExec")
}
// Combination of fork and exec, careful to be thread safe.
@@ -247,5 +230,5 @@ func Exec(argv0 string, argv []string, envv []string) (err error) {
if ret == 0 {
return nil
}
return syscall.Errno(ret)
return Errno(ret)
}

View File

@@ -0,0 +1,39 @@
// 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.
//go:build aix || darwin
package syscall
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
// forkExecPipe opens a pipe and non-atomically sets O_CLOEXEC on both file
// descriptors.
func forkExecPipe(p []int) error {
err := Pipe(p)
if err != nil {
return err
}
ret := os.Fcntl(c.Int(p[0]), syscall.F_SETFD, syscall.FD_CLOEXEC)
if ret != 0 {
return Errno(ret)
}
ret = os.Fcntl(c.Int(p[1]), syscall.F_SETFD, syscall.FD_CLOEXEC)
if ret != 0 {
return Errno(ret)
}
return nil
}
func acquireForkLock() {
ForkLock.Lock()
}
func releaseForkLock() {
ForkLock.Unlock()
}

View File

@@ -0,0 +1,52 @@
// Copyright 2022 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.
//go:build unix
package syscall
import (
"sync/atomic"
"github.com/goplus/llgo/c/syscall"
)
// origRlimitNofile, if not {0, 0}, is the original soft RLIMIT_NOFILE.
// When we can assume that we are bootstrapping with Go 1.19,
// this can be atomic.Pointer[Rlimit].
var origRlimitNofile atomic.Value // of Rlimit
// Some systems set an artificially low soft limit on open file count, for compatibility
// with code that uses select and its hard-coded maximum file descriptor
// (limited by the size of fd_set).
//
// Go does not use select, so it should not be subject to these limits.
// On some systems the limit is 256, which is very easy to run into,
// even in simple programs like gofmt when they parallelize walking
// a file tree.
//
// After a long discussion on go.dev/issue/46279, we decided the
// best approach was for Go to raise the limit unconditionally for itself,
// and then leave old software to set the limit back as needed.
// Code that really wants Go to leave the limit alone can set the hard limit,
// which Go of course has no choice but to respect.
func init() {
var lim Rlimit
if err := Getrlimit(syscall.RLIMIT_NOFILE, &lim); err == nil && lim.Cur != lim.Max {
origRlimitNofile.Store(lim)
lim.Cur = lim.Max
adjustFileLimit(&lim)
setrlimit(syscall.RLIMIT_NOFILE, &lim)
}
}
func Setrlimit(resource int, rlim *Rlimit) error {
err := setrlimit(resource, rlim)
if err == nil && resource == syscall.RLIMIT_NOFILE {
// Store zeroes in origRlimitNofile to tell StartProcess
// to not adjust the rlimit in the child process.
origRlimitNofile.Store(Rlimit{0, 0})
}
return err
}

View File

@@ -0,0 +1,20 @@
// Copyright 2022 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.
//go:build darwin
package syscall
// adjustFileLimit adds per-OS limitations on the Rlimit used for RLIMIT_NOFILE. See rlimit.go.
func adjustFileLimit(lim *Rlimit) {
// On older macOS, setrlimit(RLIMIT_NOFILE, lim) with lim.Cur = infinity fails.
// Set to the value of kern.maxfilesperproc instead.
n, err := SysctlUint32("kern.maxfilesperproc")
if err != nil {
return
}
if lim.Cur > uint64(n) {
lim.Cur = uint64(n)
}
}

View File

@@ -0,0 +1,10 @@
// Copyright 2022 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.
//go:build aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris
package syscall
// adjustFileLimit adds per-OS limitations on the Rlimit used for RLIMIT_NOFILE. See rlimit.go.
func adjustFileLimit(lim *Rlimit) {}

View File

@@ -73,24 +73,32 @@ func Getwd() (string, error) {
return "", Errno(os.Errno)
}
func Getenv(key string) (value string, found bool) {
ret := os.Getenv(c.AllocaCStr(key))
if ret != nil {
return c.GoString(ret), true
}
return "", false
}
func Getpid() (pid int) {
return int(os.Getpid())
}
func Kill(pid int, signum Signal) (err error) {
ret := os.Kill(c.Int(pid), c.Int(signum))
ret := os.Kill(os.PidT(pid), c.Int(signum))
if ret == 0 {
return nil
}
return Errno(os.Errno)
return Errno(ret)
}
func fork() (uintptr, Errno) {
ret := os.Fork()
if ret >= 0 {
return uintptr(ret), Errno(0)
}
return 0, Errno(os.Errno)
}
func wait4(pid int, wstatus *c.Int, options int, rusage *syscall.Rusage) (wpid int, err error) {
ret := os.Wait4(os.PidT(pid), wstatus, c.Int(options), rusage)
if ret >= 0 {
return int(ret), nil
}
return 0, Errno(os.Errno)
}
func Open(path string, mode int, perm uint32) (fd int, err error) {
@@ -117,12 +125,20 @@ func Read(fd int, p []byte) (n int, err error) {
return 0, Errno(os.Errno)
}
func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
ret := os.Read(c.Int(fd), unsafe.Pointer(buf), uintptr(nbuf))
if ret >= 0 {
return ret, nil // TODO(xsw): confirm err == nil (not io.EOF) when ret == 0
}
return 0, Errno(os.Errno)
}
func Close(fd int) (err error) {
ret := os.Close(c.Int(fd))
if ret == 0 {
return nil
}
return Errno(os.Errno)
return Errno(ret)
}
type Stat_t = syscall.Stat_t
@@ -132,7 +148,7 @@ func Lstat(path string, stat *Stat_t) (err error) {
if ret == 0 {
return nil
}
return Errno(os.Errno)
return Errno(ret)
}
func Stat(path string, stat *Stat_t) (err error) {
@@ -140,5 +156,37 @@ func Stat(path string, stat *Stat_t) (err error) {
if ret == 0 {
return nil
}
return Errno(os.Errno)
return Errno(ret)
}
func Pipe(p []int) (err error) {
if len(p) != 2 {
return Errno(syscall.EINVAL)
}
var q [2]c.Int
ret := os.Pipe(&q)
if ret == 0 {
p[0] = int(q[0])
p[1] = int(q[1])
return nil
}
return Errno(ret)
}
type Rlimit syscall.Rlimit
func Getrlimit(which int, lim *Rlimit) (err error) {
ret := os.Getrlimit(c.Int(which), (*syscall.Rlimit)(lim))
if ret == 0 {
return nil
}
return Errno(ret)
}
func setrlimit(which int, lim *Rlimit) (err error) {
ret := os.Setrlimit(c.Int(which), (*syscall.Rlimit)(lim))
if ret == 0 {
return nil
}
return Errno(ret)
}

View File

@@ -12,6 +12,14 @@
package syscall
import (
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
func Getgroups() (gids []int, err error) {
/* TODO(xsw):
n, err := getgroups(0, nil)
@@ -106,9 +114,13 @@ func (w WaitStatus) Signal() Signal {
func (w WaitStatus) CoreDump() bool { return w.Signaled() && w&core != 0 }
func (w WaitStatus) Stopped() bool { return w&mask == stopped && Signal(w>>shift) != SIGSTOP }
func (w WaitStatus) Stopped() bool {
return w&mask == stopped && Signal(w>>shift) != Signal(syscall.SIGSTOP)
}
func (w WaitStatus) Continued() bool { return w&mask == stopped && Signal(w>>shift) == SIGSTOP }
func (w WaitStatus) Continued() bool {
return w&mask == stopped && Signal(w>>shift) == Signal(syscall.SIGSTOP)
}
func (w WaitStatus) StopSignal() Signal {
if !w.Stopped() {
@@ -119,9 +131,8 @@ func (w WaitStatus) StopSignal() Signal {
func (w WaitStatus) TrapCause() int { return -1 }
/* TODO(xsw):
func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int, err error) {
var status _C_int
func Wait4(pid int, wstatus *WaitStatus, options int, rusage *syscall.Rusage) (wpid int, err error) {
var status c.Int
wpid, err = wait4(pid, &status, options, rusage)
if wstatus != nil {
*wstatus = WaitStatus(status)
@@ -129,6 +140,7 @@ func Wait4(pid int, wstatus *WaitStatus, options int, rusage *Rusage) (wpid int,
return
}
/* TODO(xsw):
func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) {
if sa.Port < 0 || sa.Port > 0xFFFF {
return nil, 0, EINVAL
@@ -430,8 +442,10 @@ func Sysctl(name string) (value string, err error) {
}
return string(buf[0:n]), nil
}
*/
func SysctlUint32(name string) (value uint32, err error) {
/* TODO(xsw):
// Translate name to mib number.
mib, err := nametomib(name)
if err != nil {
@@ -445,11 +459,19 @@ func SysctlUint32(name string) (value uint32, err error) {
return 0, err
}
if n != 4 {
return 0, EIO
return 0, Errno(syscall.EIO)
}
return *(*uint32)(unsafe.Pointer(&buf[0])), nil
*/
n := uintptr(4)
ret := os.Sysctlbyname(c.AllocaCStr(name), unsafe.Pointer(&value), &n, nil, 0)
if ret != 0 {
err = Errno(os.Errno)
}
return
}
/*
//sys utimes(path string, timeval *[2]Timeval) (err error)
func Utimes(path string, tv []Timeval) (err error) {

View File

@@ -10,6 +10,7 @@ import (
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/syscall"
"github.com/goplus/llgo/internal/lib/internal/oserror"
)
@@ -29,11 +30,11 @@ func (e Errno) Error() string {
func (e Errno) Is(target error) bool {
switch target {
case oserror.ErrPermission:
return e == EACCES || e == EPERM
return e == Errno(syscall.EACCES) || e == Errno(syscall.EPERM)
case oserror.ErrExist:
return e == EEXIST || e == ENOTEMPTY
return e == Errno(syscall.EEXIST) || e == Errno(syscall.ENOTEMPTY)
case oserror.ErrNotExist:
return e == ENOENT
return e == Errno(syscall.ENOENT)
// TODO(xsw): go1.21
// case errors.ErrUnsupported:
// return e == ENOSYS || e == ENOTSUP || e == EOPNOTSUPP
@@ -42,11 +43,12 @@ func (e Errno) Is(target error) bool {
}
func (e Errno) Temporary() bool {
return e == EINTR || e == EMFILE || e == ENFILE || e.Timeout()
return e == Errno(syscall.EINTR) || e == Errno(syscall.EMFILE) ||
e == Errno(syscall.ENFILE) || e.Timeout()
}
func (e Errno) Timeout() bool {
return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT
return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK) || e == Errno(syscall.ETIMEDOUT)
}
// A Signal is a number describing a process signal.

View File

@@ -1,165 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package syscall
// Errors
const (
E2BIG = Errno(0x7)
EACCES = Errno(0xd)
EADDRINUSE = Errno(0x30)
EADDRNOTAVAIL = Errno(0x31)
EAFNOSUPPORT = Errno(0x2f)
EAGAIN = Errno(0x23)
EALREADY = Errno(0x25)
EAUTH = Errno(0x50)
EBADARCH = Errno(0x56)
EBADEXEC = Errno(0x55)
EBADF = Errno(0x9)
EBADMACHO = Errno(0x58)
EBADMSG = Errno(0x5e)
EBADRPC = Errno(0x48)
EBUSY = Errno(0x10)
ECANCELED = Errno(0x59)
ECHILD = Errno(0xa)
ECONNABORTED = Errno(0x35)
ECONNREFUSED = Errno(0x3d)
ECONNRESET = Errno(0x36)
EDEADLK = Errno(0xb)
EDESTADDRREQ = Errno(0x27)
EDEVERR = Errno(0x53)
EDOM = Errno(0x21)
EDQUOT = Errno(0x45)
EEXIST = Errno(0x11)
EFAULT = Errno(0xe)
EFBIG = Errno(0x1b)
EFTYPE = Errno(0x4f)
EHOSTDOWN = Errno(0x40)
EHOSTUNREACH = Errno(0x41)
EIDRM = Errno(0x5a)
EILSEQ = Errno(0x5c)
EINPROGRESS = Errno(0x24)
EINTR = Errno(0x4)
EINVAL = Errno(0x16)
EIO = Errno(0x5)
EISCONN = Errno(0x38)
EISDIR = Errno(0x15)
ELAST = Errno(0x6a)
ELOOP = Errno(0x3e)
EMFILE = Errno(0x18)
EMLINK = Errno(0x1f)
EMSGSIZE = Errno(0x28)
EMULTIHOP = Errno(0x5f)
ENAMETOOLONG = Errno(0x3f)
ENEEDAUTH = Errno(0x51)
ENETDOWN = Errno(0x32)
ENETRESET = Errno(0x34)
ENETUNREACH = Errno(0x33)
ENFILE = Errno(0x17)
ENOATTR = Errno(0x5d)
ENOBUFS = Errno(0x37)
ENODATA = Errno(0x60)
ENODEV = Errno(0x13)
ENOENT = Errno(0x2)
ENOEXEC = Errno(0x8)
ENOLCK = Errno(0x4d)
ENOLINK = Errno(0x61)
ENOMEM = Errno(0xc)
ENOMSG = Errno(0x5b)
ENOPOLICY = Errno(0x67)
ENOPROTOOPT = Errno(0x2a)
ENOSPC = Errno(0x1c)
ENOSR = Errno(0x62)
ENOSTR = Errno(0x63)
ENOSYS = Errno(0x4e)
ENOTBLK = Errno(0xf)
ENOTCONN = Errno(0x39)
ENOTDIR = Errno(0x14)
ENOTEMPTY = Errno(0x42)
ENOTRECOVERABLE = Errno(0x68)
ENOTSOCK = Errno(0x26)
ENOTSUP = Errno(0x2d)
ENOTTY = Errno(0x19)
ENXIO = Errno(0x6)
EOPNOTSUPP = Errno(0x66)
EOVERFLOW = Errno(0x54)
EOWNERDEAD = Errno(0x69)
EPERM = Errno(0x1)
EPFNOSUPPORT = Errno(0x2e)
EPIPE = Errno(0x20)
EPROCLIM = Errno(0x43)
EPROCUNAVAIL = Errno(0x4c)
EPROGMISMATCH = Errno(0x4b)
EPROGUNAVAIL = Errno(0x4a)
EPROTO = Errno(0x64)
EPROTONOSUPPORT = Errno(0x2b)
EPROTOTYPE = Errno(0x29)
EPWROFF = Errno(0x52)
EQFULL = Errno(0x6a)
ERANGE = Errno(0x22)
EREMOTE = Errno(0x47)
EROFS = Errno(0x1e)
ERPCMISMATCH = Errno(0x49)
ESHLIBVERS = Errno(0x57)
ESHUTDOWN = Errno(0x3a)
ESOCKTNOSUPPORT = Errno(0x2c)
ESPIPE = Errno(0x1d)
ESRCH = Errno(0x3)
ESTALE = Errno(0x46)
ETIME = Errno(0x65)
ETIMEDOUT = Errno(0x3c)
ETOOMANYREFS = Errno(0x3b)
ETXTBSY = Errno(0x1a)
EUSERS = Errno(0x44)
EWOULDBLOCK = Errno(0x23)
EXDEV = Errno(0x12)
)
// Signals
const (
SIGABRT = Signal(0x6)
SIGALRM = Signal(0xe)
SIGBUS = Signal(0xa)
SIGCHLD = Signal(0x14)
SIGCONT = Signal(0x13)
SIGEMT = Signal(0x7)
SIGFPE = Signal(0x8)
SIGHUP = Signal(0x1)
SIGILL = Signal(0x4)
SIGINFO = Signal(0x1d)
SIGINT = Signal(0x2)
SIGIO = Signal(0x17)
SIGIOT = Signal(0x6)
SIGKILL = Signal(0x9)
SIGPIPE = Signal(0xd)
SIGPROF = Signal(0x1b)
SIGQUIT = Signal(0x3)
SIGSEGV = Signal(0xb)
SIGSTOP = Signal(0x11)
SIGSYS = Signal(0xc)
SIGTERM = Signal(0xf)
SIGTRAP = Signal(0x5)
SIGTSTP = Signal(0x12)
SIGTTIN = Signal(0x15)
SIGTTOU = Signal(0x16)
SIGURG = Signal(0x10)
SIGUSR1 = Signal(0x1e)
SIGUSR2 = Signal(0x1f)
SIGVTALRM = Signal(0x1a)
SIGWINCH = Signal(0x1c)
SIGXCPU = Signal(0x18)
SIGXFSZ = Signal(0x19)
)

View File

@@ -1,193 +0,0 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package syscall
// Errors
const (
E2BIG = Errno(0x7)
EACCES = Errno(0xd)
EADDRINUSE = Errno(0x62)
EADDRNOTAVAIL = Errno(0x63)
EADV = Errno(0x44)
EAFNOSUPPORT = Errno(0x61)
EAGAIN = Errno(0xb)
EALREADY = Errno(0x72)
EBADE = Errno(0x34)
EBADF = Errno(0x9)
EBADFD = Errno(0x4d)
EBADMSG = Errno(0x4a)
EBADR = Errno(0x35)
EBADRQC = Errno(0x38)
EBADSLT = Errno(0x39)
EBFONT = Errno(0x3b)
EBUSY = Errno(0x10)
ECANCELED = Errno(0x7d)
ECHILD = Errno(0xa)
ECHRNG = Errno(0x2c)
ECOMM = Errno(0x46)
ECONNABORTED = Errno(0x67)
ECONNREFUSED = Errno(0x6f)
ECONNRESET = Errno(0x68)
EDEADLK = Errno(0x23)
EDEADLOCK = Errno(0x23)
EDESTADDRREQ = Errno(0x59)
EDOM = Errno(0x21)
EDOTDOT = Errno(0x49)
EDQUOT = Errno(0x7a)
EEXIST = Errno(0x11)
EFAULT = Errno(0xe)
EFBIG = Errno(0x1b)
EHOSTDOWN = Errno(0x70)
EHOSTUNREACH = Errno(0x71)
EIDRM = Errno(0x2b)
EILSEQ = Errno(0x54)
EINPROGRESS = Errno(0x73)
EINTR = Errno(0x4)
EINVAL = Errno(0x16)
EIO = Errno(0x5)
EISCONN = Errno(0x6a)
EISDIR = Errno(0x15)
EISNAM = Errno(0x78)
EKEYEXPIRED = Errno(0x7f)
EKEYREJECTED = Errno(0x81)
EKEYREVOKED = Errno(0x80)
EL2HLT = Errno(0x33)
EL2NSYNC = Errno(0x2d)
EL3HLT = Errno(0x2e)
EL3RST = Errno(0x2f)
ELIBACC = Errno(0x4f)
ELIBBAD = Errno(0x50)
ELIBEXEC = Errno(0x53)
ELIBMAX = Errno(0x52)
ELIBSCN = Errno(0x51)
ELNRNG = Errno(0x30)
ELOOP = Errno(0x28)
EMEDIUMTYPE = Errno(0x7c)
EMFILE = Errno(0x18)
EMLINK = Errno(0x1f)
EMSGSIZE = Errno(0x5a)
EMULTIHOP = Errno(0x48)
ENAMETOOLONG = Errno(0x24)
ENAVAIL = Errno(0x77)
ENETDOWN = Errno(0x64)
ENETRESET = Errno(0x66)
ENETUNREACH = Errno(0x65)
ENFILE = Errno(0x17)
ENOANO = Errno(0x37)
ENOBUFS = Errno(0x69)
ENOCSI = Errno(0x32)
ENODATA = Errno(0x3d)
ENODEV = Errno(0x13)
ENOENT = Errno(0x2)
ENOEXEC = Errno(0x8)
ENOKEY = Errno(0x7e)
ENOLCK = Errno(0x25)
ENOLINK = Errno(0x43)
ENOMEDIUM = Errno(0x7b)
ENOMEM = Errno(0xc)
ENOMSG = Errno(0x2a)
ENONET = Errno(0x40)
ENOPKG = Errno(0x41)
ENOPROTOOPT = Errno(0x5c)
ENOSPC = Errno(0x1c)
ENOSR = Errno(0x3f)
ENOSTR = Errno(0x3c)
ENOSYS = Errno(0x26)
ENOTBLK = Errno(0xf)
ENOTCONN = Errno(0x6b)
ENOTDIR = Errno(0x14)
ENOTEMPTY = Errno(0x27)
ENOTNAM = Errno(0x76)
ENOTRECOVERABLE = Errno(0x83)
ENOTSOCK = Errno(0x58)
ENOTSUP = Errno(0x5f)
ENOTTY = Errno(0x19)
ENOTUNIQ = Errno(0x4c)
ENXIO = Errno(0x6)
EOPNOTSUPP = Errno(0x5f)
EOVERFLOW = Errno(0x4b)
EOWNERDEAD = Errno(0x82)
EPERM = Errno(0x1)
EPFNOSUPPORT = Errno(0x60)
EPIPE = Errno(0x20)
EPROTO = Errno(0x47)
EPROTONOSUPPORT = Errno(0x5d)
EPROTOTYPE = Errno(0x5b)
ERANGE = Errno(0x22)
EREMCHG = Errno(0x4e)
EREMOTE = Errno(0x42)
EREMOTEIO = Errno(0x79)
ERESTART = Errno(0x55)
ERFKILL = Errno(0x84)
EROFS = Errno(0x1e)
ESHUTDOWN = Errno(0x6c)
ESOCKTNOSUPPORT = Errno(0x5e)
ESPIPE = Errno(0x1d)
ESRCH = Errno(0x3)
ESRMNT = Errno(0x45)
ESTALE = Errno(0x74)
ESTRPIPE = Errno(0x56)
ETIME = Errno(0x3e)
ETIMEDOUT = Errno(0x6e)
ETOOMANYREFS = Errno(0x6d)
ETXTBSY = Errno(0x1a)
EUCLEAN = Errno(0x75)
EUNATCH = Errno(0x31)
EUSERS = Errno(0x57)
EWOULDBLOCK = Errno(0xb)
EXDEV = Errno(0x12)
EXFULL = Errno(0x36)
)
// Signals
const (
SIGABRT = Signal(0x6)
SIGALRM = Signal(0xe)
SIGBUS = Signal(0x7)
SIGCHLD = Signal(0x11)
SIGCLD = Signal(0x11)
SIGCONT = Signal(0x12)
SIGFPE = Signal(0x8)
SIGHUP = Signal(0x1)
SIGILL = Signal(0x4)
SIGINT = Signal(0x2)
SIGIO = Signal(0x1d)
SIGIOT = Signal(0x6)
SIGKILL = Signal(0x9)
SIGPIPE = Signal(0xd)
SIGPOLL = Signal(0x1d)
SIGPROF = Signal(0x1b)
SIGPWR = Signal(0x1e)
SIGQUIT = Signal(0x3)
SIGSEGV = Signal(0xb)
SIGSTKFLT = Signal(0x10)
SIGSTOP = Signal(0x13)
SIGSYS = Signal(0x1f)
SIGTERM = Signal(0xf)
SIGTRAP = Signal(0x5)
SIGTSTP = Signal(0x14)
SIGTTIN = Signal(0x15)
SIGTTOU = Signal(0x16)
SIGUNUSED = Signal(0x1f)
SIGURG = Signal(0x17)
SIGUSR1 = Signal(0xa)
SIGUSR2 = Signal(0xc)
SIGVTALRM = Signal(0x1a)
SIGWINCH = Signal(0x1c)
SIGXCPU = Signal(0x18)
SIGXFSZ = Signal(0x19)
)