2024-09-16 22:41:46 +00:00
|
|
|
package process
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
2024-09-17 15:41:29 +00:00
|
|
|
"log/slog"
|
2024-09-16 22:41:46 +00:00
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
2024-09-17 01:40:56 +00:00
|
|
|
"path/filepath"
|
2024-09-16 22:41:46 +00:00
|
|
|
"strings"
|
|
|
|
|
"syscall"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
2024-09-17 01:40:56 +00:00
|
|
|
var workingDir string
|
2024-09-16 22:41:46 +00:00
|
|
|
var commands = make([]*exec.Cmd, 0)
|
|
|
|
|
|
|
|
|
|
func AppendRunning(cmd *exec.Cmd) {
|
2024-09-17 15:41:29 +00:00
|
|
|
slog.Info("running", slog.String("command", strings.Join(cmd.Args, " ")))
|
2024-09-16 22:41:46 +00:00
|
|
|
commands = append(commands, cmd)
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-17 01:40:56 +00:00
|
|
|
func GetWorkingDir() string {
|
|
|
|
|
if workingDir == "" {
|
|
|
|
|
wd, _ := os.Getwd()
|
|
|
|
|
return wd
|
|
|
|
|
}
|
|
|
|
|
return workingDir
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func SetWorkingDir(dir string) {
|
|
|
|
|
workingDir = dir
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetPathRelativeToCwd(path string) string {
|
|
|
|
|
return filepath.Join(GetWorkingDir(), path)
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-16 22:41:46 +00:00
|
|
|
func KillAll() {
|
|
|
|
|
|
|
|
|
|
tries := 0
|
|
|
|
|
for {
|
|
|
|
|
tries++
|
|
|
|
|
allFinished := true
|
|
|
|
|
for _, cmd := range commands {
|
|
|
|
|
if cmd.Process == nil {
|
|
|
|
|
allFinished = false
|
|
|
|
|
|
|
|
|
|
if tries > 50 {
|
|
|
|
|
args := strings.Join(cmd.Args, " ")
|
|
|
|
|
log.Printf("process %v is not running after 50 tries, breaking.\n", args)
|
|
|
|
|
allFinished = true
|
|
|
|
|
break
|
|
|
|
|
} else {
|
|
|
|
|
time.Sleep(time.Millisecond * 50)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if allFinished {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, command := range commands {
|
|
|
|
|
pid := command.Process.Pid
|
|
|
|
|
err := syscall.Kill(-pid, syscall.SIGKILL)
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
finished := true
|
|
|
|
|
for _, c := range commands {
|
|
|
|
|
if c.Process == nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
exists, err := PidExists(int32(c.Process.Pid))
|
|
|
|
|
if err != nil {
|
|
|
|
|
finished = false
|
|
|
|
|
}
|
|
|
|
|
if exists {
|
|
|
|
|
syscall.Kill(-c.Process.Pid, syscall.SIGKILL)
|
|
|
|
|
finished = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if finished {
|
|
|
|
|
break
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Printf("waiting for all processes to exit\n")
|
|
|
|
|
time.Sleep(time.Millisecond * 5)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commands = make([]*exec.Cmd, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func PidExists(pid int32) (bool, error) {
|
|
|
|
|
if pid <= 0 {
|
|
|
|
|
return false, fmt.Errorf("invalid pid %v", pid)
|
|
|
|
|
}
|
|
|
|
|
proc, err := os.FindProcess(int(pid))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
err = proc.Signal(syscall.Signal(0))
|
|
|
|
|
if err == nil {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
if err.Error() == "os: process already finished" {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
var errno syscall.Errno
|
|
|
|
|
ok := errors.As(err, &errno)
|
|
|
|
|
if !ok {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
switch errno {
|
|
|
|
|
case syscall.ESRCH:
|
|
|
|
|
return false, nil
|
|
|
|
|
case syscall.EPERM:
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RunOrExit(command string) {
|
|
|
|
|
_ = Run(command, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RunMany(commands []string, exitOnError bool) error {
|
|
|
|
|
for _, command := range commands {
|
|
|
|
|
err := Run(command, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if exitOnError {
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Run(command string, exitOnError bool) error {
|
|
|
|
|
cmd := exec.Command("bash", "-c", command)
|
|
|
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
|
cmd.Stderr = os.Stderr
|
2024-09-17 01:40:56 +00:00
|
|
|
|
|
|
|
|
if workingDir != "" {
|
|
|
|
|
cmd.Dir = workingDir
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-16 22:41:46 +00:00
|
|
|
AppendRunning(cmd)
|
|
|
|
|
err := cmd.Run()
|
2024-09-17 15:41:29 +00:00
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
return nil
|
2024-09-16 22:41:46 +00:00
|
|
|
}
|
2024-09-17 15:41:29 +00:00
|
|
|
|
|
|
|
|
if exitOnError {
|
|
|
|
|
log.Println(fmt.Sprintf("error: %v", err))
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if strings.Contains(err.Error(), "signal: killed") {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return err
|
2024-09-16 22:41:46 +00:00
|
|
|
}
|