Skip to content

Commit

Permalink
replace raw syscall implementation with musl wrapper
Browse files Browse the repository at this point in the history
Signed-off-by: leongross <leon.gross@9elements.com>
  • Loading branch information
leongross committed Oct 28, 2024
1 parent e6fface commit 8c69425
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 12 deletions.
1 change: 1 addition & 0 deletions builder/musl.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ var libMusl = Library{
"thread/*.c",
"time/*.c",
"unistd/*.c",
"process/*.c",
}
if arch == "arm" {
// These files need to be added to the start for some reason.
Expand Down
24 changes: 23 additions & 1 deletion src/os/exec_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ var (
Kill Signal = syscall.SIGKILL
)

var (
ErrNotImplementedDir = errors.New("directory setting not implemented")
ErrNotImplementedSys = errors.New("sys setting not implemented")
ErrNotImplementedFiles = errors.New("files setting not implemented")
)

// Keep compatible with golang and always succeed and return new proc with pid on Linux.
func findProcess(pid int) (*Process, error) {
return &Process{Pid: pid}, nil
Expand Down Expand Up @@ -57,7 +63,9 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
attr = new(ProcAttr)
}

pid, err = fork()
p, err := fork()
pid = int(p)

if err != nil {
return 0, err
} else {
Expand All @@ -78,6 +86,20 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
// 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.
// It thereby replaces the newly created process with the specified command and arguments.
func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
if attr != nil {
if attr.Dir != "" {
return nil, ErrNotImplementedDir
}

if attr.Sys != nil {
return nil, ErrNotImplementedSys
}

if len(attr.Files) != 0 {
return nil, ErrNotImplementedFiles
}
}

pid, err := forkExec(name, argv, attr)
if err != nil {
return nil, err
Expand Down
37 changes: 35 additions & 2 deletions src/os/exec_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
. "os"
"runtime"
"syscall"
"testing"
)

Expand All @@ -27,10 +28,9 @@ func TestForkExec(t *testing.T) {
t.Fatalf("forkExec failed: new process has pid 0")
}
t.Logf("forkExec succeeded: new process has pid %d", proc)

}

func TestForkExecInvalid(t *testing.T) {
func TestForkExecErrNotExist(t *testing.T) {
proc, err := StartProcess("invalid", []string{"invalid"}, &ProcAttr{})
if !errors.Is(err, ErrNotExist) {
t.Fatalf("wanted ErrNotExist, got %s\n", err)
Expand All @@ -40,3 +40,36 @@ func TestForkExecInvalid(t *testing.T) {
t.Fatalf("wanted nil, got %v\n", proc)
}
}

func TestForkExecProcDir(t *testing.T) {
proc, err := StartProcess("/bin/echo", []string{"hello", "world"}, &ProcAttr{Dir: "dir"})
if !errors.Is(err, ErrNotImplementedDir) {
t.Fatalf("wanted ErrNotImplementedDir, got %v\n", err)
}

if proc != nil {
t.Fatalf("wanted nil, got %v\n", proc)
}
}

func TestForkExecProcSys(t *testing.T) {
proc, err := StartProcess("/bin/echo", []string{"hello", "world"}, &ProcAttr{Sys: &syscall.SysProcAttr{}})
if !errors.Is(err, ErrNotImplementedSys) {
t.Fatalf("wanted ErrNotImplementedSys, got %v\n", err)
}

if proc != nil {
t.Fatalf("wanted nil, got %v\n", proc)
}
}

func TestForkExecProcFiles(t *testing.T) {
proc, err := StartProcess("/bin/echo", []string{"hello", "world"}, &ProcAttr{Files: []*File{}})
if !errors.Is(err, ErrNotImplementedFiles) {
t.Fatalf("wanted ErrNotImplementedFiles, got %v\n", err)
}

if proc != nil {
t.Fatalf("wanted nil, got %v\n", proc)
}
}
23 changes: 14 additions & 9 deletions src/os/osexec.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build linux && !baremetal && !darwin && !tinygo.wasm && !arm64
//go:build linux && !baremetal && !tinygo.wasm && !arm64

// arm64 does not have a fork syscall, so ignore it for now
// TODO: add support for arm64 with clone or use musl implementation
Expand All @@ -10,15 +10,12 @@ import (
"unsafe"
)

func fork() (pid int, err error) {
ret, _, err := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
if int(ret) != 0 {
errno := err.(syscall.Errno)
if int(errno) != 0 {
return -1, errno
}
func fork() (pid int32, err error) {
pid = libc_fork()
if pid != 0 {
err = syscall.Errno(*libc_errno())
}
return int(ret), nil
return
}

// the golang standard library does not expose interfaces for execve and fork, so we define them here the same way via the libc wrapper
Expand Down Expand Up @@ -52,3 +49,11 @@ func cstring(s string) []byte {
// final byte should be zero from the initial allocation
return data
}

//export fork
func libc_fork() int32

// Internal musl function to get the C errno pointer.
//
//export __errno_location
func libc_errno() *int32

0 comments on commit 8c69425

Please sign in to comment.