Skip to content

Commit

Permalink
CLI can't stop a running sandbox #535
Browse files Browse the repository at this point in the history
  • Loading branch information
pmi authored and alansemenov committed May 7, 2024
1 parent 357fdbe commit 95d4e6b
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 27 deletions.
13 changes: 3 additions & 10 deletions internal/app/commands/sandbox/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var Start = cli.Command{
},
common.FORCE_FLAG,
},
Usage: "Start the sandbox in dev mode.",
Usage: "Start the sandbox in dev mode.",
ArgsUsage: "<name>",
Action: func(c *cli.Context) error {

Expand Down Expand Up @@ -89,6 +89,7 @@ func StartSandbox(c *cli.Context, sandbox *Sandbox, detach, devMode, debug bool,

if isSandboxRunning {
if rData.Running == sandbox.Name && ((rData.Mode == common.MODE_DEV) == devMode) {
fmt.Fprintf(os.Stderr, "\nSandbox '%s' is already running.\n\n", sandbox.Name)
return nil, true
} else if !AskToStopSandbox(rData, force) {
return errors.New(fmt.Sprintf("Sandbox '%s' is already running in %s mode", rData.Running, rData.Mode)), true
Expand All @@ -110,15 +111,7 @@ func StartSandbox(c *cli.Context, sandbox *Sandbox, detach, devMode, debug bool,

cmd := startDistro(sandbox.Distro, sandbox.Name, detach, devMode, debug)

var pid int
if !detach {
// current process' PID
pid = os.Getpid()
} else {
// current process will finish so use detached process' PID
pid = cmd.Process.Pid
}
writeRunningSandbox(sandbox.Name, pid, devMode)
writeRunningSandbox(sandbox.Name, cmd.Process.Pid, devMode)

if !detach {
util.ListenForInterrupt(func() {
Expand Down
138 changes: 138 additions & 0 deletions internal/app/util/system/system_mac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//go:build darwin
// +build darwin

package system

import (
"bytes"
"fmt"
"os"
"os/exec"
"os/user"
"strconv"
"strings"
"syscall"
)

const detachedProcName = "java"

func prepareCmd(app string, args []string) *exec.Cmd {
cmd := exec.Command(app, args...)
cmd.SysProcAttr = &syscall.SysProcAttr{}
return cmd
}

func setCommandLineParams(cmd *exec.Cmd, app string, args []string) {
// they have already been set in the prepareCmd
}

func setStartAttachedParams(cmd *exec.Cmd) {
// cmd.SysProcAttr.Setpgid = true
}

func setStartDetachedParams(cmd *exec.Cmd) {
cmd.SysProcAttr.Noctty = false
cmd.SysProcAttr.Setpgid = true
}

func checkWriteAccess(path string) bool {
if info, err := os.Stat(path); err == nil {
mode := info.Mode()
if mode&(1<<1) != 0 {
// anyone has write access
return true
} else {
stat := info.Sys().(*syscall.Stat_t)
me, meErr := user.Current()
if meErr != nil {
return false
}
if mode&(1<<7) != 0 {
// author has write access, check if current user is author
authorId := strconv.FormatUint(uint64(stat.Uid), 10)
if me.Uid == authorId {
return true
}
} else if mode&(1<<4) != 0 {
// members of file group has write access, check if current user belongs to it
fileGroupId := int(stat.Gid)
myGroupIds, groupsErr := me.GroupIds()
if groupsErr != nil {
return false
}
for myGroupId, _ := range myGroupIds {
if myGroupId == fileGroupId {
return true
}
}
}
}
}
return false
}

/*
* Taken from https://blog.csdn.net/fyxichen/article/details/51857864
*/

func Setpgid(pid, pgid int) error {
return syscall.Setpgid(pid, pgid)
}

func Kill(pids []int) {
for _, pid := range pids {
if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
fmt.Printf("Failed to kill process %d: %s\n", pid, err)
// Continue trying to kill other processes
}
}
}

func KillAll(pid int) error {
pids, err := findChildPIDs(pid)
if err != nil {
return err
}
// Append the parent PID at the end to ensure it gets killed last
pids = append(pids, pid)

Kill(pids)

return nil
}

func findChildPIDs(parentPID int) ([]int, error) {
var pids []int
var out bytes.Buffer
// Using 'ps' to get child processes of a given PID
cmd := exec.Command("pgrep", "-P", strconv.Itoa(parentPID))
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
errCode := (err.(*exec.ExitError)).ExitCode()
if errCode == 1 {
// No child processes found
return pids, nil
} else if errCode == 2 {
// Syntax error
return nil, err
}
}
// Parse the output to get the child PIDs
for _, pidStr := range strings.Split(out.String(), "\n") {
pidStr = strings.TrimSpace(pidStr)
if pidStr != "" {
pid, err := strconv.Atoi(pidStr)
if err != nil {
continue // skip individual errors
}
pids = append(pids, pid)
// Recursively find children of this child
childPIDs, err := findChildPIDs(pid)
if err == nil {
pids = append(pids, childPIDs...)
}
}
}
return pids, nil
}
18 changes: 9 additions & 9 deletions internal/app/util/system/system_others.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//go:build !windows
// +build !windows
//go:build !windows && !darwin
// +build !windows,!darwin

package system

import (
"fmt"
"os"
"os/exec"
"os/user"
Expand Down Expand Up @@ -76,16 +77,15 @@ func Setpgid(pid, pgid int) error {
return syscall.Setpgid(pid, pgid)
}

func Getppids(pid int) ([]uint32, error) {
return []uint32{}, nil
}

func Kill(pids []uint32) {
func Kill(pids []int) {
for _, pid := range pids {
syscall.Kill(int(pid), syscall.SIGTERM)
if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
fmt.Printf("Failed to kill process %d: %s\n", pid, err)
// Continue trying to kill other processes
}
}
}

func KillAll(pid int) error {
return syscall.Kill(pid-(pid*2), syscall.SIGTERM)
return syscall.Kill(-pid, syscall.SIGTERM)
}
21 changes: 13 additions & 8 deletions internal/app/util/system/system_wins.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,42 +93,43 @@ func Setpgid(pid, pgid int) error {
}

func KillAll(pid int) error {
pids := Getppids(uint32(pid))
pids := getppids(pid)
Kill(pids)
return nil
}

func Kill(pids []uint32) {
func Kill(pids []int) {
for _, pid := range pids {
pro, err := os.FindProcess(int(pid))
pro, err := os.FindProcess(pid)
if err != nil {
continue
}
pro.Kill()
}
}

func Getppids(pid uint32) []uint32 {
func getppids(pid int) []int {
infos, err := GetProcs()
if err != nil {
return []uint32{pid}
return []int{pid}
}
var pids []uint32 = make([]uint32, 0, len(infos))
var pids = make([]int, 0, len(infos))
var index int = 0
pids = append(pids, pid)

var length int = len(pids)
for index < length {
for _, info := range infos {
if info.PPid == pids[index] {
pids = append(pids, info.Pid)
if int(info.PPid) == pids[index] {
pids = append(pids, int(info.Pid))
}
}
index += 1
length = len(pids)
}
return pids
}

func GetProcs() (procs []ProcessInfo, err error) {
snap := createToolhelp32Snapshot(TH32CS_SNAPPROCESS, uint32(0))
if snap == 0 {
Expand All @@ -148,6 +149,7 @@ func GetProcs() (procs []ProcessInfo, err error) {
}
return
}

func createToolhelp32Snapshot(flags, processId uint32) HANDLE {
ret, _, _ := procCreateToolhelp32Snapshot.Call(
uintptr(flags),
Expand All @@ -157,18 +159,21 @@ func createToolhelp32Snapshot(flags, processId uint32) HANDLE {
}
return HANDLE(ret)
}

func process32First(snapshot HANDLE, pe *PROCESSENTRY32) bool {
ret, _, _ := procProcess32First.Call(
uintptr(snapshot),
uintptr(unsafe.Pointer(pe)))
return ret != 0
}

func process32Next(snapshot HANDLE, pe *PROCESSENTRY32) bool {
ret, _, _ := procProcess32Next.Call(
uintptr(snapshot),
uintptr(unsafe.Pointer(pe)))
return ret != 0
}

func closeHandle(object HANDLE) bool {
ret, _, _ := procCloseHandle.Call(
uintptr(object))
Expand Down

0 comments on commit 95d4e6b

Please sign in to comment.