Skip to content

Commit e12c2de

Browse files
committed
add minified StartProcess implementation (hacky)
Signed-off-by: leongross <[email protected]>
1 parent 2d5a8d4 commit e12c2de

File tree

3 files changed

+92
-1
lines changed

3 files changed

+92
-1
lines changed

src/os/exec.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@ type Process struct {
5757
Pid int
5858
}
5959

60+
// Create a lightweight implementation of execve / syscall.StartProcess
61+
// Do not save all the proc info as golang does but make some sanity checks
6062
func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) {
61-
return nil, &PathError{Op: "fork/exec", Path: name, Err: ErrNotImplemented}
63+
return startProcess(name, argv, attr)
6264
}
6365

6466
func (p *Process) Wait() (*ProcessState, error) {
@@ -92,3 +94,7 @@ func (p *Process) Release() error {
9294
func FindProcess(pid int) (*Process, error) {
9395
return findProcess(pid)
9496
}
97+
98+
func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
99+
return forkExec(argv0, argv, attr)
100+
}

src/os/exec_posix.go

+53
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package os
99
import (
1010
"runtime"
1111
"syscall"
12+
"unsafe"
1213
)
1314

1415
// The only signal values guaranteed to be present in the os package on all
@@ -33,3 +34,55 @@ func (p *Process) release() error {
3334
runtime.SetFinalizer(p, nil)
3435
return nil
3536
}
37+
38+
// Combination of fork and exec, careful to be thread safe.
39+
40+
// https://cs.opensource.google/go/go/+/master:src/syscall/exec_unix.go;l=143?q=forkExec&ss=go%2Fgo
41+
// losely inspired by the golang implementation
42+
// This is a hacky fire-and forget implementation without setting any attributes, using pipes or checking for errors
43+
func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
44+
// Convert args to C form.
45+
var (
46+
ret uintptr
47+
)
48+
49+
argv0p, err := syscall.BytePtrFromString(argv0)
50+
if err != nil {
51+
return 0, err
52+
}
53+
argvp, err := syscall.SlicePtrFromStrings(argv)
54+
if err != nil {
55+
return 0, err
56+
}
57+
envvp, err := syscall.SlicePtrFromStrings(attr.Env)
58+
if err != nil {
59+
return 0, err
60+
}
61+
62+
// pid, _, _ = syscall.Syscall6(syscall.SYS_FORK, 0, 0, 0, 0, 0, 0)
63+
// 1. fork
64+
ret, _, _ = syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
65+
if ret != 0 {
66+
// parent
67+
return int(ret), nil
68+
} else {
69+
// 2. exec
70+
ret, _, _ = syscall.Syscall6(syscall.SYS_EXECVE, uintptr(unsafe.Pointer(argv0p)), uintptr(unsafe.Pointer(&argvp[0])), uintptr(unsafe.Pointer(&envvp[0])), 0, 0, 0)
71+
if ret != 0 {
72+
// exec failed
73+
syscall.Exit(1)
74+
}
75+
// 3. TODO: use pipes to communicate back child status
76+
return int(ret), nil
77+
}
78+
}
79+
80+
// in regular go this is where the forkExec thingy comes in play
81+
func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
82+
pid, err := ForkExec(name, argv, attr)
83+
if err != nil {
84+
return nil, err
85+
}
86+
87+
return findProcess(pid)
88+
}

src/syscall/syscall.go

+32
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package syscall
33
import (
44
"errors"
55
"sync/atomic"
6+
"internal/bytealg"
7+
8+
"github.com/tinygo-org/tinygo/src/internal/bytealg"
69
)
710

811
const (
@@ -11,6 +14,11 @@ const (
1114
AF_INET6 = 0xa
1215
)
1316

17+
const (
18+
// #define EINVAL 22 /* Invalid argument */
19+
EINVAL = 22
20+
)
21+
1422
func Exit(code int)
1523

1624
type Rlimit struct {
@@ -26,3 +34,27 @@ var origRlimitNofile atomic.Value // of Rlimit
2634
func Setrlimit(resource int, rlim *Rlimit) error {
2735
return errors.New("Setrlimit not implemented")
2836
}
37+
38+
// ByteSliceFromString returns a NUL-terminated slice of bytes
39+
// containing the text of s. If s contains a NUL byte at any
40+
// location, it returns (nil, [EINVAL]).
41+
// https://cs.opensource.google/go/go/+/master:src/syscall/syscall.go;l=45;drc=94982a07825aec711f11c97283e99e467838d616
42+
func ByteSliceFromString(s string) ([]byte, error) {
43+
if bytealg.IndexByteString(s, 0) != -1 {
44+
return nil, errors.New("contains NUL")
45+
}
46+
a := make([]byte, len(s)+1)
47+
copy(a, s)
48+
return a, nil
49+
}
50+
51+
// BytePtrFromString returns a pointer to a NUL-terminated array of
52+
// bytes containing the text of s. If s contains a NUL byte at any
53+
// location, it returns (nil, [EINVAL]).
54+
func BytePtrFromString(s string) (*byte, error) {
55+
a, err := ByteSliceFromString(s)
56+
if err != nil {
57+
return nil, err
58+
}
59+
return &a[0], nil
60+
}

0 commit comments

Comments
 (0)