154 lines
3.9 KiB
Go
154 lines
3.9 KiB
Go
package process
|
|
|
|
import (
|
|
"context"
|
|
"syscall"
|
|
"time"
|
|
|
|
"dappco.re/go/core"
|
|
)
|
|
|
|
// --- ACTION messages (broadcast via Core.ACTION) ---
|
|
|
|
// ActionProcessStarted is broadcast when a process begins execution.
|
|
type ActionProcessStarted struct {
|
|
ID string
|
|
Command string
|
|
Args []string
|
|
Dir string
|
|
PID int
|
|
}
|
|
|
|
// ActionProcessOutput is broadcast for each line of output.
|
|
// Subscribe to this for real-time streaming.
|
|
type ActionProcessOutput struct {
|
|
ID string
|
|
Line string
|
|
Stream Stream
|
|
}
|
|
|
|
// ActionProcessExited is broadcast when a process completes.
|
|
// Check ExitCode for success (0) or failure.
|
|
type ActionProcessExited struct {
|
|
ID string
|
|
ExitCode int
|
|
Duration time.Duration
|
|
Error error // Non-nil if failed to start or was killed
|
|
}
|
|
|
|
// ActionProcessKilled is broadcast when a process is terminated.
|
|
type ActionProcessKilled struct {
|
|
ID string
|
|
Signal string
|
|
}
|
|
|
|
// --- Core Action Handlers ---------------------------------------------------
|
|
|
|
func (s *Service) handleRun(ctx context.Context, opts core.Options) core.Result {
|
|
command := opts.String("command")
|
|
if command == "" {
|
|
return core.Result{Value: core.E("process.run", "command is required", nil), OK: false}
|
|
}
|
|
|
|
runOpts := RunOptions{
|
|
Command: command,
|
|
Dir: opts.String("dir"),
|
|
}
|
|
if r := opts.Get("args"); r.OK {
|
|
runOpts.Args = optionStrings(r.Value)
|
|
}
|
|
if r := opts.Get("env"); r.OK {
|
|
runOpts.Env = optionStrings(r.Value)
|
|
}
|
|
|
|
return s.runCommand(ctx, runOpts)
|
|
}
|
|
|
|
func (s *Service) handleStart(ctx context.Context, opts core.Options) core.Result {
|
|
command := opts.String("command")
|
|
if command == "" {
|
|
return core.Result{Value: core.E("process.start", "command is required", nil), OK: false}
|
|
}
|
|
|
|
runOpts := RunOptions{
|
|
Command: command,
|
|
Dir: opts.String("dir"),
|
|
Detach: opts.Bool("detach"),
|
|
}
|
|
if r := opts.Get("args"); r.OK {
|
|
runOpts.Args = optionStrings(r.Value)
|
|
}
|
|
if r := opts.Get("env"); r.OK {
|
|
runOpts.Env = optionStrings(r.Value)
|
|
}
|
|
|
|
r := s.StartWithOptions(ctx, runOpts)
|
|
if !r.OK {
|
|
return r
|
|
}
|
|
return core.Result{Value: r.Value.(*ManagedProcess).ID, OK: true}
|
|
}
|
|
|
|
func (s *Service) handleKill(_ context.Context, opts core.Options) core.Result {
|
|
id := opts.String("id")
|
|
if id != "" {
|
|
if err := s.Kill(id); err != nil {
|
|
if core.Is(err, ErrProcessNotFound) {
|
|
return core.Result{Value: core.E("process.kill", core.Concat("not found: ", id), nil), OK: false}
|
|
}
|
|
return core.Result{Value: core.E("process.kill", core.Concat("kill failed: ", id), err), OK: false}
|
|
}
|
|
return core.Result{OK: true}
|
|
}
|
|
|
|
pid := opts.Int("pid")
|
|
if pid > 0 {
|
|
proc, err := processHandle(pid)
|
|
if err != nil {
|
|
return core.Result{Value: core.E("process.kill", core.Concat("find pid failed: ", core.Sprintf("%d", pid)), err), OK: false}
|
|
}
|
|
if err := proc.Signal(syscall.SIGTERM); err != nil {
|
|
return core.Result{Value: core.E("process.kill", core.Concat("signal failed: ", core.Sprintf("%d", pid)), err), OK: false}
|
|
}
|
|
return core.Result{OK: true}
|
|
}
|
|
|
|
return core.Result{Value: core.E("process.kill", "need id or pid", nil), OK: false}
|
|
}
|
|
|
|
func (s *Service) handleList(_ context.Context, _ core.Options) core.Result {
|
|
return core.Result{Value: s.managed.Names(), OK: true}
|
|
}
|
|
|
|
func (s *Service) handleGet(_ context.Context, opts core.Options) core.Result {
|
|
id := opts.String("id")
|
|
if id == "" {
|
|
return core.Result{Value: core.E("process.get", "id is required", nil), OK: false}
|
|
}
|
|
proc, err := s.Get(id)
|
|
if err != nil {
|
|
return core.Result{Value: core.E("process.get", core.Concat("not found: ", id), err), OK: false}
|
|
}
|
|
return core.Result{Value: proc.Info(), OK: true}
|
|
}
|
|
|
|
func optionStrings(value any) []string {
|
|
switch typed := value.(type) {
|
|
case nil:
|
|
return nil
|
|
case []string:
|
|
return append([]string(nil), typed...)
|
|
case []any:
|
|
result := make([]string, 0, len(typed))
|
|
for _, item := range typed {
|
|
text, ok := item.(string)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
result = append(result, text)
|
|
}
|
|
return result
|
|
default:
|
|
return nil
|
|
}
|
|
}
|