Skip to content

Commit 73c5ddd

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

File tree

3 files changed

+91
-1
lines changed

3 files changed

+91
-1
lines changed

src/os/exec.go

Lines changed: 7 additions & 1 deletion
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

Lines changed: 53 additions & 0 deletions
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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package syscall
22

33
import (
44
"errors"
5+
6+
"github.com/tinygo-org/tinygo/src/internal/bytealg"
57
)
68

79
const (
@@ -10,6 +12,11 @@ const (
1012
AF_INET6 = 0xa
1113
)
1214

15+
const (
16+
// #define EINVAL 22 /* Invalid argument */
17+
EINVAL = 22
18+
)
19+
1320
func Exit(code int)
1421

1522
type Rlimit struct {
@@ -20,3 +27,27 @@ type Rlimit struct {
2027
func Setrlimit(resource int, rlim *Rlimit) error {
2128
return errors.New("Setrlimit not implemented")
2229
}
30+
31+
// ByteSliceFromString returns a NUL-terminated slice of bytes
32+
// containing the text of s. If s contains a NUL byte at any
33+
// location, it returns (nil, [EINVAL]).
34+
// https://cs.opensource.google/go/go/+/master:src/syscall/syscall.go;l=45;drc=94982a07825aec711f11c97283e99e467838d616
35+
func ByteSliceFromString(s string) ([]byte, error) {
36+
if bytealg.IndexByteString(s, 0) != -1 {
37+
return nil, errors.New("contains NUL")
38+
}
39+
a := make([]byte, len(s)+1)
40+
copy(a, s)
41+
return a, nil
42+
}
43+
44+
// BytePtrFromString returns a pointer to a NUL-terminated array of
45+
// bytes containing the text of s. If s contains a NUL byte at any
46+
// location, it returns (nil, [EINVAL]).
47+
func BytePtrFromString(s string) (*byte, error) {
48+
a, err := ByteSliceFromString(s)
49+
if err != nil {
50+
return nil, err
51+
}
52+
return &a[0], nil
53+
}

0 commit comments

Comments
 (0)