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
|
//go:linkname Environ environ
|
||||||
func Environ() **c.Char
|
var Environ **c.Char
|
||||||
|
|
||||||
//go:linkname Getenv C.getenv
|
//go:linkname Getenv C.getenv
|
||||||
func Getenv(name *c.Char) *c.Char
|
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
|
//go:linkname Kill C.kill
|
||||||
func Kill(pid c.Int, sig c.Int) c.Int
|
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 {
|
func supportedInternal(name string) bool {
|
||||||
return strings.HasPrefix(name, "abi.") || strings.HasPrefix(name, "bytealg.") ||
|
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,24 +673,22 @@ func canSkipToBuild(pkgPath string) bool {
|
|||||||
type none struct{}
|
type none struct{}
|
||||||
|
|
||||||
var hasAltPkg = map[string]none{
|
var hasAltPkg = map[string]none{
|
||||||
//"errors": {},
|
"fmt": {},
|
||||||
"fmt": {},
|
"internal/abi": {},
|
||||||
"internal/abi": {},
|
"internal/bytealg": {},
|
||||||
"internal/bytealg": {},
|
"internal/oserror": {},
|
||||||
"internal/oserror": {},
|
"internal/reflectlite": {},
|
||||||
"internal/reflectlite": {},
|
"internal/syscall/execenv": {},
|
||||||
//"io": {},
|
"math": {},
|
||||||
//"io/fs": {},
|
"math/cmplx": {},
|
||||||
"math": {},
|
"reflect": {},
|
||||||
"math/cmplx": {},
|
"sync": {},
|
||||||
"reflect": {},
|
"sync/atomic": {},
|
||||||
"sync": {},
|
"syscall": {},
|
||||||
"sync/atomic": {},
|
"time": {},
|
||||||
"syscall": {},
|
"os": {},
|
||||||
"time": {},
|
"os/exec": {},
|
||||||
"os": {},
|
"runtime": {},
|
||||||
"os/exec": {},
|
|
||||||
"runtime": {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var overlayFiles = map[string]string{
|
var overlayFiles = map[string]string{
|
||||||
|
|||||||
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 (
|
import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/goplus/llgo/internal/lib/internal/syscall/execenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The only signal values guaranteed to be present in the os package on all
|
// 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) {
|
func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
|
||||||
/* TODO(xsw):
|
|
||||||
// If there is no SysProcAttr (ie. no Chroot or changed
|
// If there is no SysProcAttr (ie. no Chroot or changed
|
||||||
// UID/GID), double-check existence of the directory we want
|
// UID/GID), double-check existence of the directory we want
|
||||||
// to chdir into. We can make the error clearer this way.
|
// 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)
|
pid, h, e := syscall.StartProcess(name, argv, sysattr)
|
||||||
|
|
||||||
|
// TODO(xsw):
|
||||||
// Make sure we don't run the finalizers of attr.Files.
|
// Make sure we don't run the finalizers of attr.Files.
|
||||||
runtime.KeepAlive(attr)
|
// runtime.KeepAlive(attr)
|
||||||
|
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return nil, &PathError{Op: "fork/exec", Path: name, Err: e}
|
return nil, &PathError{Op: "fork/exec", Path: name, Err: e}
|
||||||
}
|
}
|
||||||
|
|
||||||
return newProcess(pid, h), nil
|
return newProcess(pid, h), nil
|
||||||
*/
|
|
||||||
panic("todo: os.startProcess")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Process) kill() error {
|
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