c/os: fork/exec
This commit is contained in:
20
_demo/cexec/exec.go
Normal file
20
_demo/cexec/exec.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ls := c.Str("ls")
|
||||
args := []*c.Char{ls, c.Str("-l"), nil}
|
||||
if runtime.GOOS == "windows" {
|
||||
ls = c.Str("dir")
|
||||
args = []*c.Char{ls, nil}
|
||||
}
|
||||
|
||||
os.Execvp(ls, unsafe.SliceData(args))
|
||||
}
|
||||
26
_demo/sysexec/exec.go
Normal file
26
_demo/sysexec/exec.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ls := "ls"
|
||||
args := []string{"ls", "-l"}
|
||||
if runtime.GOOS == "windows" {
|
||||
ls = "dir"
|
||||
args = nil
|
||||
}
|
||||
lspath, _ := exec.LookPath(ls)
|
||||
if lspath != "" {
|
||||
ls = lspath
|
||||
}
|
||||
err := syscall.Exec(ls, args, nil)
|
||||
if err != nil {
|
||||
fmt.Println("syscall.Exec error:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
36
c/os/os.go
36
c/os/os.go
@@ -108,8 +108,8 @@ func Chroot(path *c.Char) c.Int
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
//go:linkname Environ C.environ
|
||||
func Environ() **c.Char
|
||||
//go:linkname Environ environ
|
||||
var Environ **c.Char
|
||||
|
||||
//go:linkname Getenv C.getenv
|
||||
func Getenv(name *c.Char) *c.Char
|
||||
@@ -208,6 +208,38 @@ func Isatty(fd c.Int) c.Int
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Execl(const char *path, const char *arg0, ..., /*, (char *)0, */)
|
||||
//
|
||||
// Execl requires the full path of the program to be provided.
|
||||
//
|
||||
//go:linkname Execl C.execl
|
||||
func Execl(path *c.Char, __llgo_va_list ...any) c.Int
|
||||
|
||||
// Execle(const char *path, const char *arg0, ..., /* (char *)0, char *const envp[] */)
|
||||
//
|
||||
//go:linkname Execle C.execle
|
||||
func Execle(path *c.Char, __llgo_va_list ...any) c.Int
|
||||
|
||||
// Execlp(const char *file, const char *arg0, ..., /*, (char *)0, */)
|
||||
//
|
||||
// Execlp only needs to provide the program name and it will search for the program in the
|
||||
// paths specified in the PATH environment variable.
|
||||
//
|
||||
//go:linkname Execlp C.execlp
|
||||
func Execlp(file *c.Char, __llgo_va_list ...any) c.Int
|
||||
|
||||
//go:linkname Execv C.execv
|
||||
func Execv(path *c.Char, argv **c.Char) c.Int
|
||||
|
||||
//go:linkname Execve C.execve
|
||||
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
|
||||
|
||||
//go:linkname Fork C.fork
|
||||
func Fork() c.Int
|
||||
|
||||
//go:linkname Kill C.kill
|
||||
func Kill(pid c.Int, sig c.Int) c.Int
|
||||
|
||||
|
||||
21
c/syscall/syscall.go
Normal file
21
c/syscall/syscall.go
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
const (
|
||||
LLGoPackage = "decl"
|
||||
)
|
||||
@@ -531,7 +531,8 @@ func ignoreName(name string) bool {
|
||||
|
||||
func supportedInternal(name string) bool {
|
||||
return strings.HasPrefix(name, "abi.") || strings.HasPrefix(name, "bytealg.") ||
|
||||
strings.HasPrefix(name, "oserror.") || strings.HasPrefix(name, "reflectlite.")
|
||||
strings.HasPrefix(name, "oserror.") || strings.HasPrefix(name, "reflectlite.") ||
|
||||
strings.HasPrefix(name, "syscall/execenv.")
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -673,14 +673,12 @@ func canSkipToBuild(pkgPath string) bool {
|
||||
type none struct{}
|
||||
|
||||
var hasAltPkg = map[string]none{
|
||||
//"errors": {},
|
||||
"fmt": {},
|
||||
"internal/abi": {},
|
||||
"internal/bytealg": {},
|
||||
"internal/oserror": {},
|
||||
"internal/reflectlite": {},
|
||||
//"io": {},
|
||||
//"io/fs": {},
|
||||
"internal/syscall/execenv": {},
|
||||
"math": {},
|
||||
"math/cmplx": {},
|
||||
"reflect": {},
|
||||
|
||||
20
internal/lib/internal/syscall/execenv/execenv_default.go
Normal file
20
internal/lib/internal/syscall/execenv/execenv_default.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2020 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 !windows
|
||||
|
||||
package execenv
|
||||
|
||||
// llgo:skipall
|
||||
import "syscall"
|
||||
|
||||
// Default will return the default environment
|
||||
// variables based on the process attributes
|
||||
// provided.
|
||||
//
|
||||
// Defaults to syscall.Environ() on all platforms
|
||||
// other than Windows.
|
||||
func Default(sys *syscall.SysProcAttr) ([]string, error) {
|
||||
return syscall.Environ(), nil
|
||||
}
|
||||
48
internal/lib/internal/syscall/execenv/execenv_windows.go
Normal file
48
internal/lib/internal/syscall/execenv/execenv_windows.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2020 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 windows
|
||||
|
||||
package execenv
|
||||
|
||||
// llgo:skipall
|
||||
import (
|
||||
"internal/syscall/windows"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Default will return the default environment
|
||||
// variables based on the process attributes
|
||||
// provided.
|
||||
//
|
||||
// If the process attributes contain a token, then
|
||||
// the environment variables will be sourced from
|
||||
// the defaults for that user token, otherwise they
|
||||
// will be sourced from syscall.Environ().
|
||||
func Default(sys *syscall.SysProcAttr) (env []string, err error) {
|
||||
if sys == nil || sys.Token == 0 {
|
||||
return syscall.Environ(), nil
|
||||
}
|
||||
var blockp *uint16
|
||||
err = windows.CreateEnvironmentBlock(&blockp, sys.Token, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer windows.DestroyEnvironmentBlock(blockp)
|
||||
|
||||
const size = unsafe.Sizeof(*blockp)
|
||||
for *blockp != 0 { // environment block ends with empty string
|
||||
// find NUL terminator
|
||||
end := unsafe.Add(unsafe.Pointer(blockp), size)
|
||||
for *(*uint16)(end) != 0 {
|
||||
end = unsafe.Add(end, size)
|
||||
}
|
||||
|
||||
entry := unsafe.Slice(blockp, (uintptr(end)-uintptr(unsafe.Pointer(blockp)))/2)
|
||||
env = append(env, syscall.UTF16ToString(entry))
|
||||
blockp = (*uint16)(unsafe.Add(end, size))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -8,6 +8,8 @@ package os
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/goplus/llgo/internal/lib/internal/syscall/execenv"
|
||||
)
|
||||
|
||||
// The only signal values guaranteed to be present in the os package on all
|
||||
@@ -21,7 +23,6 @@ var (
|
||||
)
|
||||
|
||||
func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
|
||||
/* TODO(xsw):
|
||||
// If there is no SysProcAttr (ie. no Chroot or changed
|
||||
// UID/GID), double-check existence of the directory we want
|
||||
// to chdir into. We can make the error clearer this way.
|
||||
@@ -51,16 +52,15 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
|
||||
|
||||
pid, h, e := syscall.StartProcess(name, argv, sysattr)
|
||||
|
||||
// TODO(xsw):
|
||||
// Make sure we don't run the finalizers of attr.Files.
|
||||
runtime.KeepAlive(attr)
|
||||
// runtime.KeepAlive(attr)
|
||||
|
||||
if e != nil {
|
||||
return nil, &PathError{Op: "fork/exec", Path: name, Err: e}
|
||||
}
|
||||
|
||||
return newProcess(pid, h), nil
|
||||
*/
|
||||
panic("todo: os.startProcess")
|
||||
}
|
||||
|
||||
func (p *Process) kill() error {
|
||||
|
||||
319
internal/lib/syscall/exec_libc.go
Normal file
319
internal/lib/syscall/exec_libc.go
Normal file
@@ -0,0 +1,319 @@
|
||||
// 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 || solaris
|
||||
|
||||
// This file handles forkAndExecInChild function for OS using libc syscall like AIX or Solaris.
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type SysProcAttr struct {
|
||||
Chroot string // Chroot.
|
||||
Credential *Credential // Credential.
|
||||
Setsid bool // Create session.
|
||||
// Setpgid sets the process group ID of the child to Pgid,
|
||||
// or, if Pgid == 0, to the new child's process ID.
|
||||
Setpgid bool
|
||||
// Setctty sets the controlling terminal of the child to
|
||||
// file descriptor Ctty. Ctty must be a descriptor number
|
||||
// in the child process: an index into ProcAttr.Files.
|
||||
// This is only meaningful if Setsid is true.
|
||||
Setctty bool
|
||||
Noctty bool // Detach fd 0 from controlling terminal
|
||||
Ctty int // Controlling TTY fd
|
||||
// Foreground places the child process group in the foreground.
|
||||
// This implies Setpgid. The Ctty field must be set to
|
||||
// the descriptor of the controlling TTY.
|
||||
// Unlike Setctty, in this case Ctty must be a descriptor
|
||||
// number in the parent process.
|
||||
Foreground bool
|
||||
Pgid int // Child's process group ID if Setpgid.
|
||||
}
|
||||
|
||||
// Implemented in runtime package.
|
||||
func runtime_BeforeFork()
|
||||
func runtime_AfterFork()
|
||||
func runtime_AfterForkInChild()
|
||||
|
||||
func chdir(path uintptr) (err Errno)
|
||||
func chroot1(path uintptr) (err Errno)
|
||||
func closeFD(fd uintptr) (err Errno)
|
||||
func dup2child(old uintptr, new uintptr) (val uintptr, err Errno)
|
||||
func execve(path uintptr, argv uintptr, envp uintptr) (err Errno)
|
||||
func exit(code uintptr)
|
||||
func fcntl1(fd uintptr, cmd uintptr, arg uintptr) (val uintptr, err Errno)
|
||||
func forkx(flags uintptr) (pid uintptr, err Errno)
|
||||
func getpid() (pid uintptr, err Errno)
|
||||
func ioctl(fd uintptr, req uintptr, arg uintptr) (err Errno)
|
||||
func setgid(gid uintptr) (err Errno)
|
||||
func setgroups1(ngid uintptr, gid uintptr) (err Errno)
|
||||
func setrlimit1(which uintptr, lim unsafe.Pointer) (err Errno)
|
||||
func setsid() (pid uintptr, err Errno)
|
||||
func setuid(uid uintptr) (err Errno)
|
||||
func setpgid(pid uintptr, pgid uintptr) (err Errno)
|
||||
func write1(fd uintptr, buf uintptr, nbyte uintptr) (n uintptr, err Errno)
|
||||
|
||||
// syscall defines this global on our behalf to avoid a build dependency on other platforms
|
||||
func init() {
|
||||
execveLibc = execve
|
||||
}
|
||||
|
||||
// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
|
||||
// If a dup or exec fails, write the errno error to pipe.
|
||||
// (Pipe 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.
|
||||
//
|
||||
// We call hand-crafted syscalls, implemented in
|
||||
// ../runtime/syscall_solaris.go, rather than generated libc wrappers
|
||||
// because we need to avoid lazy-loading the functions (might malloc,
|
||||
// split the stack, or acquire mutexes). We can't call RawSyscall
|
||||
// because it's not safe even for BSD-subsystem calls.
|
||||
//
|
||||
//go:norace
|
||||
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
|
||||
// Declare all variables at top in case any
|
||||
// declarations require heap allocation (e.g., err1).
|
||||
var (
|
||||
r1 uintptr
|
||||
err1 Errno
|
||||
nextfd int
|
||||
i int
|
||||
pgrp _Pid_t
|
||||
cred *Credential
|
||||
ngroups, groups uintptr
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
nextfd++
|
||||
|
||||
// About to call fork.
|
||||
// No more allocation or calls of non-assembly functions.
|
||||
runtime_BeforeFork()
|
||||
r1, err1 = forkx(0x1) // FORK_NOSIGCHLD
|
||||
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.
|
||||
|
||||
// Session ID
|
||||
if sys.Setsid {
|
||||
_, err1 = setsid()
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Set process group
|
||||
if sys.Setpgid || sys.Foreground {
|
||||
// Place child in process group.
|
||||
err1 = setpgid(0, uintptr(sys.Pgid))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
if sys.Foreground {
|
||||
pgrp = _Pid_t(sys.Pgid)
|
||||
if pgrp == 0 {
|
||||
r1, err1 = getpid()
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
pgrp = _Pid_t(r1)
|
||||
}
|
||||
|
||||
// Place process group in foreground.
|
||||
err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = chroot1(uintptr(unsafe.Pointer(chroot)))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = setgroups1(ngroups, groups)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
err1 = setgid(uintptr(cred.Gid))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
err1 = setuid(uintptr(cred.Uid))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Chdir
|
||||
if dir != nil {
|
||||
err1 = chdir(uintptr(unsafe.Pointer(dir)))
|
||||
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 {
|
||||
switch runtime.GOOS {
|
||||
case "illumos", "solaris":
|
||||
_, err1 = fcntl1(uintptr(pipe), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
|
||||
default:
|
||||
_, err1 = dup2child(uintptr(pipe), uintptr(nextfd))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
_, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
|
||||
}
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
pipe = nextfd
|
||||
nextfd++
|
||||
}
|
||||
for i = 0; i < len(fd); i++ {
|
||||
if fd[i] >= 0 && fd[i] < i {
|
||||
if nextfd == pipe { // don't stomp on pipe
|
||||
nextfd++
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "illumos", "solaris":
|
||||
_, err1 = fcntl1(uintptr(fd[i]), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
|
||||
default:
|
||||
_, err1 = dup2child(uintptr(fd[i]), uintptr(nextfd))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
_, err1 = fcntl1(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 {
|
||||
closeFD(uintptr(i))
|
||||
continue
|
||||
}
|
||||
if fd[i] == i {
|
||||
// dup2(i, i) won't clear close-on-exec flag on Linux,
|
||||
// probably not elsewhere either.
|
||||
_, err1 = fcntl1(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 = dup2child(uintptr(fd[i]), uintptr(i))
|
||||
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++ {
|
||||
closeFD(uintptr(i))
|
||||
}
|
||||
|
||||
// Detach fd 0 from tty
|
||||
if sys.Noctty {
|
||||
err1 = ioctl(0, uintptr(TIOCNOTTY), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Set the controlling TTY to Ctty
|
||||
if sys.Setctty {
|
||||
// On AIX, TIOCSCTTY is undefined
|
||||
if TIOCSCTTY == 0 {
|
||||
err1 = ENOSYS
|
||||
goto childerror
|
||||
}
|
||||
err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Restore original rlimit.
|
||||
if rlimOK && rlim.Cur != 0 {
|
||||
setrlimit1(RLIMIT_NOFILE, unsafe.Pointer(&rlim))
|
||||
}
|
||||
|
||||
// Time to exec.
|
||||
err1 = execve(
|
||||
uintptr(unsafe.Pointer(argv0)),
|
||||
uintptr(unsafe.Pointer(&argv[0])),
|
||||
uintptr(unsafe.Pointer(&envv[0])))
|
||||
|
||||
childerror:
|
||||
// send error code on pipe
|
||||
write1(uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
|
||||
for {
|
||||
exit(253)
|
||||
}
|
||||
}
|
||||
|
||||
func ioctlPtr(fd, req uintptr, arg unsafe.Pointer) (err Errno) {
|
||||
return ioctl(fd, req, uintptr(arg))
|
||||
}
|
||||
290
internal/lib/syscall/exec_libc2.go
Normal file
290
internal/lib/syscall/exec_libc2.go
Normal file
@@ -0,0 +1,290 @@
|
||||
// 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 darwin || (openbsd && !mips64)
|
||||
|
||||
package syscall
|
||||
|
||||
type SysProcAttr struct {
|
||||
Chroot string // Chroot.
|
||||
Credential *Credential // Credential.
|
||||
Ptrace bool // Enable tracing.
|
||||
Setsid bool // Create session.
|
||||
// Setpgid sets the process group ID of the child to Pgid,
|
||||
// or, if Pgid == 0, to the new child's process ID.
|
||||
Setpgid bool
|
||||
// Setctty sets the controlling terminal of the child to
|
||||
// file descriptor Ctty. Ctty must be a descriptor number
|
||||
// in the child process: an index into ProcAttr.Files.
|
||||
// This is only meaningful if Setsid is true.
|
||||
Setctty bool
|
||||
Noctty bool // Detach fd 0 from controlling terminal
|
||||
Ctty int // Controlling TTY fd
|
||||
// Foreground places the child process group in the foreground.
|
||||
// This implies Setpgid. The Ctty field must be set to
|
||||
// the descriptor of the controlling TTY.
|
||||
// Unlike Setctty, in this case Ctty must be a descriptor
|
||||
// number in the parent process.
|
||||
Foreground bool
|
||||
Pgid int // Child's process group ID if Setpgid.
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// Implemented in runtime package.
|
||||
func runtime_BeforeFork()
|
||||
func runtime_AfterFork()
|
||||
func runtime_AfterForkInChild()
|
||||
*/
|
||||
|
||||
// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
|
||||
// If a dup or exec fails, write the errno error to pipe.
|
||||
// (Pipe 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.
|
||||
// For the same reason compiler does not race instrument it.
|
||||
// The calls to rawSyscall are okay because they are assembly
|
||||
// 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
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
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)
|
||||
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.
|
||||
|
||||
// Enable tracing if requested.
|
||||
if sys.Ptrace {
|
||||
if err = ptrace(PTRACE_TRACEME, 0, 0, 0); err != nil {
|
||||
err1 = err.(Errno)
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Session ID
|
||||
if sys.Setsid {
|
||||
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setsid_trampoline), 0, 0, 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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Chdir
|
||||
if dir != nil {
|
||||
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_chdir_trampoline), uintptr(unsafe.Pointer(dir)), 0, 0)
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
pipe = nextfd
|
||||
nextfd++
|
||||
}
|
||||
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 {
|
||||
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++ {
|
||||
rawSyscall(abi.FuncPCABI0(libc_close_trampoline), uintptr(i), 0, 0)
|
||||
}
|
||||
|
||||
// Detach fd 0 from tty
|
||||
if sys.Noctty {
|
||||
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), 0, uintptr(TIOCNOTTY), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// Restore original rlimit.
|
||||
if rlimOK && rlim.Cur != 0 {
|
||||
rawSyscall(abi.FuncPCABI0(libc_setrlimit_trampoline), uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(&rlim)), 0)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
*/
|
||||
panic("todo: syscall.forkAndExecInChild")
|
||||
}
|
||||
283
internal/lib/syscall/exec_unix.go
Normal file
283
internal/lib/syscall/exec_unix.go
Normal file
@@ -0,0 +1,283 @@
|
||||
// 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.
|
||||
|
||||
//go:build unix
|
||||
|
||||
// Fork, exec, wait, etc.
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ForkLock is used to synchronize 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:
|
||||
//
|
||||
// - Pipe. Use pipe2 if available. Otherwise, does not block,
|
||||
// so use ForkLock.
|
||||
// - Socket. Use SOCK_CLOEXEC if available. Otherwise, does not
|
||||
// block, so use ForkLock.
|
||||
// - Open. Use O_CLOEXEC if available. Otherwise, may block,
|
||||
// so live with the race.
|
||||
// - Dup. Use F_DUPFD_CLOEXEC or dup3 if available. Otherwise,
|
||||
// does not block, so use ForkLock.
|
||||
var ForkLock sync.RWMutex
|
||||
|
||||
func CloseOnExec(fd int) {
|
||||
/* TODO(xsw):
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC)
|
||||
*/
|
||||
panic("todo: syscall.CloseOnExec")
|
||||
}
|
||||
|
||||
func SetNonblock(fd int, nonblocking bool) (err error) {
|
||||
/* TODO(xsw):
|
||||
flag, err := fcntl(fd, F_GETFL, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if nonblocking {
|
||||
flag |= O_NONBLOCK
|
||||
} else {
|
||||
flag &^= O_NONBLOCK
|
||||
}
|
||||
_, err = fcntl(fd, F_SETFL, flag)
|
||||
return err
|
||||
*/
|
||||
panic("todo: syscall.SetNonblock")
|
||||
}
|
||||
|
||||
// Credential holds user and group identities to be assumed
|
||||
// by a child process started by StartProcess.
|
||||
type Credential struct {
|
||||
Uid uint32 // User ID.
|
||||
Gid uint32 // Group ID.
|
||||
Groups []uint32 // Supplementary group IDs.
|
||||
NoSetGroups bool // If true, don't set supplementary groups
|
||||
}
|
||||
|
||||
// ProcAttr holds attributes that will be applied to a new process started
|
||||
// by StartProcess.
|
||||
type ProcAttr struct {
|
||||
Dir string // Current working directory.
|
||||
Env []string // Environment.
|
||||
Files []uintptr // File descriptors.
|
||||
Sys *SysProcAttr
|
||||
}
|
||||
|
||||
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
|
||||
var wstatus WaitStatus
|
||||
|
||||
if attr == nil {
|
||||
attr = &zeroProcAttr
|
||||
}
|
||||
sys := attr.Sys
|
||||
if sys == nil {
|
||||
sys = &zeroSysProcAttr
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
if (runtime.GOOS == "freebsd" || runtime.GOOS == "dragonfly") && len(argv) > 0 && len(argv[0]) > len(argv0) {
|
||||
argvp[0] = argv0p
|
||||
}
|
||||
|
||||
var chroot *byte
|
||||
if sys.Chroot != "" {
|
||||
chroot, err = BytePtrFromString(sys.Chroot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
var dir *byte
|
||||
if attr.Dir != "" {
|
||||
dir, err = BytePtrFromString(attr.Dir)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
if sys.Setctty && sys.Ctty >= len(attr.Files) {
|
||||
return 0, errorspkg.New("Setctty set but Ctty not valid in child")
|
||||
}
|
||||
|
||||
acquireForkLock()
|
||||
|
||||
// Allocate child status pipe close on exec.
|
||||
if err = forkExecPipe(p[:]); err != nil {
|
||||
releaseForkLock()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Kick off child.
|
||||
pid, err1 = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1])
|
||||
if err1 != 0 {
|
||||
Close(p[0])
|
||||
Close(p[1])
|
||||
releaseForkLock()
|
||||
return 0, Errno(err1)
|
||||
}
|
||||
releaseForkLock()
|
||||
|
||||
// Read child error status from pipe.
|
||||
Close(p[1])
|
||||
for {
|
||||
n, err = readlen(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1)))
|
||||
if err != EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
Close(p[0])
|
||||
if err != nil || n != 0 {
|
||||
if n == int(unsafe.Sizeof(err1)) {
|
||||
err = Errno(err1)
|
||||
}
|
||||
if err == nil {
|
||||
err = 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 {
|
||||
_, err1 = Wait4(pid, &wstatus, 0, nil)
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 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.
|
||||
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 int, handle uintptr, err error) {
|
||||
pid, err = forkExec(argv0, argv, attr)
|
||||
return pid, 0, err
|
||||
}
|
||||
|
||||
/* TODO(xsw):
|
||||
// Implemented in runtime package.
|
||||
func runtime_BeforeExec()
|
||||
func runtime_AfterExec()
|
||||
|
||||
// execveLibc is non-nil on OS using libc syscall, set to execve in exec_libc.go; this
|
||||
// avoids a build dependency for other platforms.
|
||||
var execveLibc func(path uintptr, argv uintptr, envp uintptr) Errno
|
||||
var execveDarwin func(path *byte, argv **byte, envp **byte) error
|
||||
var execveOpenBSD func(path *byte, argv **byte, envp **byte) error
|
||||
*/
|
||||
|
||||
// Exec invokes the execve(2) system call.
|
||||
func Exec(argv0 string, argv []string, envv []string) (err error) {
|
||||
/* TODO(xsw):
|
||||
argv0p, err := BytePtrFromString(argv0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
argvp, err := SlicePtrFromStrings(argv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envvp, err := SlicePtrFromStrings(envv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runtime_BeforeExec()
|
||||
|
||||
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
|
||||
if rlimOK && rlim.Cur != 0 {
|
||||
Setrlimit(RLIMIT_NOFILE, &rlim)
|
||||
}
|
||||
|
||||
var err1 error
|
||||
if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "aix" {
|
||||
// RawSyscall should never be used on Solaris, illumos, or AIX.
|
||||
err1 = execveLibc(
|
||||
uintptr(unsafe.Pointer(argv0p)),
|
||||
uintptr(unsafe.Pointer(&argvp[0])),
|
||||
uintptr(unsafe.Pointer(&envvp[0])))
|
||||
} else if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
|
||||
// Similarly on Darwin.
|
||||
err1 = execveDarwin(argv0p, &argvp[0], &envvp[0])
|
||||
} else if runtime.GOOS == "openbsd" && (runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
|
||||
// Similarly on OpenBSD.
|
||||
err1 = execveOpenBSD(argv0p, &argvp[0], &envvp[0])
|
||||
} else {
|
||||
_, _, err1 = RawSyscall(SYS_EXECVE,
|
||||
uintptr(unsafe.Pointer(argv0p)),
|
||||
uintptr(unsafe.Pointer(&argvp[0])),
|
||||
uintptr(unsafe.Pointer(&envvp[0])))
|
||||
}
|
||||
runtime_AfterExec()
|
||||
return err1
|
||||
*/
|
||||
panic("todo: syscall.Exec")
|
||||
}
|
||||
Reference in New Issue
Block a user