1 package pipe
2 3 import (
4 "io"
5 "os"
6 "os/exec"
7 "runtime"
8 "syscall"
9 10 "github.com/p9c/p9/pkg/qu"
11 )
12 13 type Worker struct {
14 Cmd *exec.Cmd
15 Args []string
16 // Stderr io.WriteCloser
17 // StdPipe io.ReadCloser
18 StdConn *StdConn
19 }
20 21 // Spawn starts up an arbitrary executable file with given arguments and
22 // attaches a connection to its stdin/stdout
23 func Spawn(quit qu.C, args ...string) (w *Worker, e error) {
24 w = &Worker{
25 Cmd: exec.Command(args[0], args[1:]...),
26 Args: args,
27 }
28 var cmdOut io.ReadCloser
29 if cmdOut, e = w.Cmd.StdoutPipe(); E.Chk(e) {
30 return
31 }
32 var cmdIn io.WriteCloser
33 if cmdIn, e = w.Cmd.StdinPipe(); E.Chk(e) {
34 return
35 }
36 w.StdConn = New(cmdOut, cmdIn, quit)
37 w.Cmd.Stderr = os.Stderr
38 if e = w.Cmd.Start(); E.Chk(e) {
39 }
40 return
41 }
42 43 // Wait for the process to finish running
44 func (w *Worker) Wait() (e error) {
45 return w.Cmd.Wait()
46 }
47 48 // Interrupt the child process.
49 // This invokes kill on windows because windows doesn't have an interrupt
50 // signal.
51 func (w *Worker) Interrupt() (e error) {
52 if runtime.GOOS == "windows" {
53 if e = w.Cmd.Process.Kill(); E.Chk(e) {
54 }
55 return
56 }
57 if e = w.Cmd.Process.Signal(syscall.SIGINT); !E.Chk(e) {
58 D.Ln("interrupted")
59 }
60 return
61 }
62 63 // Kill forces the child process to shut down without cleanup
64 func (w *Worker) Kill() (e error) {
65 if e = w.Cmd.Process.Kill(); !E.Chk(e) {
66 D.Ln("killed")
67 }
68 return
69 }
70 71 // Stop signals the worker to shut down cleanly.
72 //
73 // Note that the worker must have handlers for os.Signal messages.
74 //
75 // It is possible and neater to put a quit method in the IPC API and use the quit channel built into the StdConn
76 func (w *Worker) Stop() (e error) {
77 return w.Cmd.Process.Signal(os.Interrupt)
78 }
79