mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-17 15:12:24 +02:00
183 lines
3.9 KiB
Go
183 lines
3.9 KiB
Go
package processguard
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const guardianArg = "--cyberstrike-internal-process-guardian"
|
|
const childArg = "--cyberstrike-internal-process-child"
|
|
|
|
type watchRequest struct {
|
|
Op string
|
|
PID int
|
|
Path string
|
|
Name string
|
|
Options Options
|
|
}
|
|
type watchReply struct {
|
|
PID int
|
|
Error string
|
|
}
|
|
|
|
type watchdog struct {
|
|
mu sync.Mutex
|
|
cmd *exec.Cmd
|
|
input *os.File
|
|
output *os.File
|
|
encoder *json.Encoder
|
|
decoder *json.Decoder
|
|
done chan struct{}
|
|
failed error
|
|
}
|
|
|
|
// The re-exec modes run before application configuration, listeners or MCP
|
|
// initialization. Stdin is a private pipe; no network control port is opened.
|
|
func init() {
|
|
if len(os.Args) < 2 {
|
|
return
|
|
}
|
|
switch os.Args[1] {
|
|
case guardianArg:
|
|
err := guardianMain(json.NewDecoder(os.Stdin), json.NewEncoder(os.Stdout))
|
|
if err != nil {
|
|
_ = json.NewEncoder(os.Stdout).Encode(watchReply{Error: err.Error()})
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
os.Exit(0)
|
|
case childArg:
|
|
if err := gatedChildMain(os.Args[2:]); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
os.Exit(0)
|
|
}
|
|
}
|
|
|
|
func startWatchdog(req watchRequest, onExit func()) (*watchdog, error) {
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cmd := exec.Command(exe, guardianArg)
|
|
configureGuardian(cmd)
|
|
in, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
in.Close()
|
|
return nil, err
|
|
}
|
|
// No inherited stderr pipe that could keep a caller's output reader alive.
|
|
if err = cmd.Start(); err != nil {
|
|
in.Close()
|
|
out.Close()
|
|
return nil, err
|
|
}
|
|
w := &watchdog{cmd: cmd, input: in.(*os.File), output: out.(*os.File), done: make(chan struct{})}
|
|
w.encoder = json.NewEncoder(w.input)
|
|
w.decoder = json.NewDecoder(w.output)
|
|
// The guardian only exits after EOF or failure; exit invalidates all RPCs.
|
|
go func() {
|
|
_ = cmd.Wait()
|
|
close(w.done)
|
|
if onExit != nil {
|
|
onExit()
|
|
}
|
|
}()
|
|
req.Op = "init"
|
|
if _, err = w.send(req); err != nil {
|
|
w.close()
|
|
return nil, err
|
|
}
|
|
return w, nil
|
|
}
|
|
|
|
func (w *watchdog) send(req watchRequest) (watchReply, error) {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
if w.failed != nil {
|
|
return watchReply{}, w.failed
|
|
}
|
|
type response struct {
|
|
reply watchReply
|
|
err error
|
|
}
|
|
result := make(chan response, 1)
|
|
// Pipe deadlines are not supported by every Windows pipe implementation.
|
|
// On timeout kill the helper and close both ends to release this goroutine.
|
|
go func() {
|
|
if err := w.encoder.Encode(req); err != nil {
|
|
result <- response{err: err}
|
|
return
|
|
}
|
|
var reply watchReply
|
|
err := w.decoder.Decode(&reply)
|
|
if err == nil && reply.Error != "" {
|
|
err = fmt.Errorf("process guardian: %s", reply.Error)
|
|
}
|
|
result <- response{reply, err}
|
|
}()
|
|
select {
|
|
case r := <-result:
|
|
w.failed = r.err
|
|
return r.reply, r.err
|
|
case <-time.After(3 * time.Second):
|
|
_ = w.cmd.Process.Kill()
|
|
_ = w.input.Close()
|
|
_ = w.output.Close()
|
|
w.failed = fmt.Errorf("process guardian acknowledgement timed out")
|
|
return watchReply{}, w.failed
|
|
}
|
|
}
|
|
|
|
func (w *watchdog) close() error {
|
|
w.mu.Lock()
|
|
_ = w.input.Close()
|
|
w.mu.Unlock()
|
|
select {
|
|
case <-w.done:
|
|
case <-time.After(3 * time.Second):
|
|
_ = w.cmd.Process.Kill()
|
|
select {
|
|
case <-w.done:
|
|
case <-time.After(3 * time.Second):
|
|
return fmt.Errorf("process guardian did not exit")
|
|
}
|
|
}
|
|
_ = w.output.Close()
|
|
return nil
|
|
}
|
|
|
|
func serveGuardian(dec *json.Decoder, enc *json.Encoder, apply func(watchRequest) error, cleanup func() error) error {
|
|
defer cleanup()
|
|
if err := enc.Encode(watchReply{PID: os.Getpid()}); err != nil {
|
|
return err
|
|
}
|
|
for {
|
|
var req watchRequest
|
|
if err := dec.Decode(&req); err != nil {
|
|
if err == io.EOF {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
err := apply(req)
|
|
reply := watchReply{PID: os.Getpid()}
|
|
if err != nil {
|
|
reply.Error = err.Error()
|
|
}
|
|
if err := enc.Encode(reply); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|