termtap/internal/app/process.go
2026-04-14 21:58:56 -07:00

77 lines
1.7 KiB
Go

package app
import (
"errors"
"fmt"
"os"
"os/exec"
"syscall"
"time"
"termtap.dev/internal/model"
"termtap.dev/internal/process"
)
func StartProcess(cmd model.Command, addr string, ch chan<- model.Message, sigCh <-chan os.Signal) {
ch <- model.Message{
Type: model.MessageTypeProcessStarting,
Body: fmt.Sprintf("spawning process '%s'", process.CommandString(cmd)),
}
proc := process.NewProcess(cmd, addr, ch)
if err := proc.Exec.Start(); err != nil {
ch <- model.Message{
Type: model.MessageTypeProcessExited,
Body: fmt.Sprintf("%q", err),
}
return
}
process.UpdateStatus(proc, true, ch)
// Listen for SIGTERM from main process
go func() {
sig := <-sigCh
ch <- model.Message{
Type: model.MessageTypeProcessSignaled,
Body: fmt.Sprintf("process with pid '%d' is being killed", proc.Exec.Process.Pid),
PID: proc.Exec.Process.Pid,
}
if proc.Exec != nil {
_ = process.SignalProcess(proc.Exec, sig)
go func() {
time.Sleep(1500 * time.Millisecond)
if process.ProcessAlive(proc.Exec) {
_ = process.SignalProcess(proc.Exec, syscall.SIGKILL)
}
}()
process.UpdateStatus(proc, false, ch)
}
}()
if err := proc.Exec.Wait(); err != nil {
if exitErr, ok := errors.AsType[*exec.ExitError](err); ok {
ch <- model.Message{
Type: model.MessageTypeProcessExited,
Body: fmt.Sprintf("process pid '%d' exited by itself", proc.Exec.Process.Pid),
PID: proc.Exec.Process.Pid,
ExitCode: exitErr.ExitCode(),
}
process.UpdateStatus(proc, false, ch)
return
}
ch <- model.Message{
Type: model.MessageTypeFatal,
Body: fmt.Sprintf("%q", err),
}
process.UpdateStatus(proc, false, ch)
return
}
}