Skip to content

Commit 9f94438

Browse files
committed
add StartProcess functionality (fix)
1 parent f10ba39 commit 9f94438

File tree

6 files changed

+170
-35
lines changed

6 files changed

+170
-35
lines changed

src/os/exec_linux.go

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Copyright 2009 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
//go:build posix || aix || linux || dragonfly || freebsd || (js && wasm) || netbsd || openbsd || solaris || wasip1
6+
// +build posix aix linux dragonfly freebsd js,wasm netbsd openbsd solaris wasip1
7+
8+
package os
9+
10+
import (
11+
"errors"
12+
"runtime"
13+
"syscall"
14+
)
15+
16+
// The only signal values guaranteed to be present in the os package on all
17+
// systems are os.Interrupt (send the process an interrupt) and os.Kill (force
18+
// the process to exit). On Windows, sending os.Interrupt to a process with
19+
// os.Process.Signal is not implemented; it will return an error instead of
20+
// sending a signal.
21+
var (
22+
Interrupt Signal = syscall.SIGINT
23+
Kill Signal = syscall.SIGKILL
24+
)
25+
26+
// Keep compatible with golang and always succeed and return new proc with pid on Linux.
27+
func findProcess(pid int) (*Process, error) {
28+
return &Process{Pid: pid}, nil
29+
}
30+
31+
func (p *Process) release() error {
32+
// NOOP for unix.
33+
p.Pid = -1
34+
// no need for a finalizer anymore
35+
runtime.SetFinalizer(p, nil)
36+
return nil
37+
}
38+
39+
// This function is a wrapper around the forkExec function, which is a wrapper around the fork and execve system calls.
40+
// The StartProcess function creates a new process by forking the current process and then calling execve to replace the current process with the new process.
41+
// It thereby replaces the newly created process with the specified command and arguments.
42+
// Differences to upstream golang implementation (https://cs.opensource.google/go/go/+/master:src/syscall/exec_unix.go;l=143):
43+
// * No setting of Process Attributes
44+
// * Ignoring Ctty
45+
// * No ForkLocking (might be introduced by #4273)
46+
// * No parent-child communication via pipes (TODO)
47+
// * No waiting for crashes child processes to prohibit zombie process accumulation / Wait status checking (TODO)
48+
func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
49+
var (
50+
ret uintptr
51+
)
52+
53+
if len(argv) == 0 {
54+
return 0, errors.New("exec: no argv")
55+
}
56+
57+
if attr == nil {
58+
attr = new(ProcAttr)
59+
}
60+
61+
argv0p, err := syscall.BytePtrFromString(argv0)
62+
if err != nil {
63+
return 0, err
64+
}
65+
argvp, err := syscall.SlicePtrFromStrings(argv)
66+
if err != nil {
67+
return 0, err
68+
}
69+
envp, err := syscall.SlicePtrFromStrings(attr.Env)
70+
if err != nil {
71+
return 0, err
72+
}
73+
74+
if (runtime.GOOS == "freebsd" || runtime.GOOS == "dragonfly") && len(argv) > 0 && len(argv[0]) > len(argv0) {
75+
argvp[0] = argv0p
76+
}
77+
78+
pid = syscall.Fork()
79+
if ret < 0 {
80+
return 0, errors.New("fork failed")
81+
}
82+
83+
if ret != 0 {
84+
// if fd == 0 code runs in parent
85+
return int(ret), nil
86+
} else {
87+
// else code runs in child, which then should exec the new process
88+
ret = syscall.Execve(argv0, argv, envp)
89+
if ret != 0 {
90+
// exec failed
91+
syscall.Exit(1)
92+
}
93+
// 3. TODO: use pipes to communicate back child status
94+
return int(ret), nil
95+
}
96+
}
97+
98+
// In Golang, the idiomatic way to create a new process is to use the StartProcess function.
99+
// Since the Model of operating system processes in tinygo differs from the one in Golang, we need to implement the StartProcess function differently.
100+
// The startProcess function is a wrapper around the forkExec function, which is a wrapper around the fork and execve system calls.
101+
// The StartProcess function creates a new process by forking the current process and then calling execve to replace the current process with the new process.
102+
// It thereby replaces the newly created process with the specified command and arguments.
103+
func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
104+
pid, err := ForkExec(name, argv, attr)
105+
if err != nil {
106+
return nil, err
107+
}
108+
109+
return findProcess(pid)
110+
}

src/os/exec_other.go

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//go:build !aix && !android && !freebsd && !linux && !netbsd && !openbsd && !plan9 && !solaris
2+
// +build !aix,!android,!freebsd,!linux,!netbsd,!openbsd,!plan9,!solaris
3+
4+
package os
5+
6+
func findProcess(pid int) (*Process, error) {
7+
return &Process{Pid: pid}, nil
8+
}
9+
10+
func (p *Process) release() error {
11+
p.Pid = -1
12+
return nil
13+
}
14+
15+
func forkExec(_ string, _ []string, _ *ProcAttr) (pid int, err error) {
16+
return 0, ErrNotImplemented
17+
}
18+
19+
func startProcess(_ string, _ []string, _ *ProcAttr) (proc *Process, err error) {
20+
return &Process{Pid: 0}, ErrNotImplemented
21+
}

src/os/exec_posix.go

-35
This file was deleted.

src/os/exec_posix_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package os_test
2+
3+
import (
4+
. "os"
5+
"runtime"
6+
"testing"
7+
)
8+
9+
// Test the functionality of the forkExec function, which is used to fork and exec a new process.
10+
// This test is not run on Windows, as forkExec is not supported on Windows.
11+
// This test is not run on Plan 9, as forkExec is not supported on Plan 9.
12+
func TestForkExec(t *testing.T) {
13+
if runtime.GOOS != "linux" {
14+
t.Logf("skipping test on %s", runtime.GOOS)
15+
return
16+
}
17+
18+
proc, err := StartProcess("echo", []string{"echo", "hello", "world"}, &ProcAttr{})
19+
if err != nil {
20+
t.Fatalf("forkExec failed: %v", err)
21+
return
22+
}
23+
24+
if proc.Pid == 0 {
25+
t.Fatalf("forkExec failed: new process has pid 0")
26+
} else {
27+
t.Logf("forkExec succeeded: new process has pid %d", proc)
28+
}
29+
}

src/syscall/exec_unix.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build unix
2+
3+
package syscall
4+
5+
// Implemented in runtime package.
6+
func runtime_BeforeExec()
7+
func runtime_AfterExec()

src/syscall/syscall_unix.go

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//go:build linux || unix
2+
// +build linux unix
3+
14
package syscall
25

36
import "errors"

0 commit comments

Comments
 (0)