Skip to content

Commit

Permalink
add StartProcess functionality (fix)
Browse files Browse the repository at this point in the history
  • Loading branch information
leongross committed Jul 31, 2024
1 parent f10ba39 commit 9f94438
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 35 deletions.
110 changes: 110 additions & 0 deletions src/os/exec_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// 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 posix || aix || linux || dragonfly || freebsd || (js && wasm) || netbsd || openbsd || solaris || wasip1
// +build posix aix linux dragonfly freebsd js,wasm netbsd openbsd solaris wasip1

package os

import (
"errors"
"runtime"
"syscall"
)

// The only signal values guaranteed to be present in the os package on all
// systems are os.Interrupt (send the process an interrupt) and os.Kill (force
// the process to exit). On Windows, sending os.Interrupt to a process with
// os.Process.Signal is not implemented; it will return an error instead of
// sending a signal.
var (
Interrupt Signal = syscall.SIGINT
Kill Signal = syscall.SIGKILL
)

// 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
}

func (p *Process) release() error {
// NOOP for unix.
p.Pid = -1
// no need for a finalizer anymore
runtime.SetFinalizer(p, nil)
return nil
}

// This function is a wrapper around the forkExec function, which is a wrapper around the fork and execve system calls.
// 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.
// Differences to upstream golang implementation (https://cs.opensource.google/go/go/+/master:src/syscall/exec_unix.go;l=143):
// * No setting of Process Attributes
// * Ignoring Ctty
// * No ForkLocking (might be introduced by #4273)
// * No parent-child communication via pipes (TODO)
// * No waiting for crashes child processes to prohibit zombie process accumulation / Wait status checking (TODO)
func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
var (
ret uintptr
)

if len(argv) == 0 {
return 0, errors.New("exec: no argv")
}

if attr == nil {
attr = new(ProcAttr)
}

argv0p, err := syscall.BytePtrFromString(argv0)
if err != nil {
return 0, err
}
argvp, err := syscall.SlicePtrFromStrings(argv)
if err != nil {
return 0, err
}
envp, err := syscall.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
}

pid = syscall.Fork()
if ret < 0 {
return 0, errors.New("fork failed")
}

if ret != 0 {
// if fd == 0 code runs in parent
return int(ret), nil
} else {
// else code runs in child, which then should exec the new process
ret = syscall.Execve(argv0, argv, envp)
if ret != 0 {
// exec failed
syscall.Exit(1)
}
// 3. TODO: use pipes to communicate back child status
return int(ret), nil
}
}

// In Golang, the idiomatic way to create a new process is to use the StartProcess function.
// Since the Model of operating system processes in tinygo differs from the one in Golang, we need to implement the StartProcess function differently.
// The startProcess function is a wrapper around the forkExec function, which is a wrapper around the fork and execve system calls.
// 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) {
pid, err := ForkExec(name, argv, attr)
if err != nil {
return nil, err
}

return findProcess(pid)
}
21 changes: 21 additions & 0 deletions src/os/exec_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//go:build !aix && !android && !freebsd && !linux && !netbsd && !openbsd && !plan9 && !solaris
// +build !aix,!android,!freebsd,!linux,!netbsd,!openbsd,!plan9,!solaris

package os

func findProcess(pid int) (*Process, error) {
return &Process{Pid: pid}, nil
}

func (p *Process) release() error {
p.Pid = -1
return nil
}

func forkExec(_ string, _ []string, _ *ProcAttr) (pid int, err error) {
return 0, ErrNotImplemented
}

func startProcess(_ string, _ []string, _ *ProcAttr) (proc *Process, err error) {
return &Process{Pid: 0}, ErrNotImplemented
}
35 changes: 0 additions & 35 deletions src/os/exec_posix.go

This file was deleted.

29 changes: 29 additions & 0 deletions src/os/exec_posix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package os_test

import (
. "os"
"runtime"
"testing"
)

// Test the functionality of the forkExec function, which is used to fork and exec a new process.
// This test is not run on Windows, as forkExec is not supported on Windows.
// This test is not run on Plan 9, as forkExec is not supported on Plan 9.
func TestForkExec(t *testing.T) {
if runtime.GOOS != "linux" {
t.Logf("skipping test on %s", runtime.GOOS)
return
}

proc, err := StartProcess("echo", []string{"echo", "hello", "world"}, &ProcAttr{})
if err != nil {
t.Fatalf("forkExec failed: %v", err)
return
}

if proc.Pid == 0 {
t.Fatalf("forkExec failed: new process has pid 0")
} else {
t.Logf("forkExec succeeded: new process has pid %d", proc)
}
}
7 changes: 7 additions & 0 deletions src/syscall/exec_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//go:build unix

package syscall

// Implemented in runtime package.
func runtime_BeforeExec()
func runtime_AfterExec()
3 changes: 3 additions & 0 deletions src/syscall/syscall_unix.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//go:build linux || unix
// +build linux unix

package syscall

import "errors"
Expand Down

0 comments on commit 9f94438

Please sign in to comment.