13 "github.com/Sirupsen/logrus"
14 "github.com/googollee/go-socket.io"
17 // EmitOutputCB is the function callback used to emit data
18 type EmitOutputCB func(sid string, cmdID int, stdout, stderr string, data *map[string]interface{})
20 // EmitExitCB is the function callback used to emit exit proc code
21 type EmitExitCB func(sid string, cmdID int, code int, err error, data *map[string]interface{})
24 // https://github.com/gorilla/websocket/blob/master/examples/command/main.go
26 // ExecPipeWs executes a command and redirect stdout/stderr into a WebSocket
27 func ExecPipeWs(cmd []string, env []string, so *socketio.Socket, sid string, cmdID int,
28 cmdExecTimeout int, log *logrus.Logger, eoCB EmitOutputCB, eeCB EmitExitCB, data *map[string]interface{}) error {
30 outr, outw, err := os.Pipe()
32 return fmt.Errorf("Pipe stdout error: " + err.Error())
35 // XXX - do we need to pipe stdin one day ?
36 inr, inw, err := os.Pipe()
40 return fmt.Errorf("Pipe stdin error: " + err.Error())
43 bashArgs := []string{"/bin/bash", "-c", strings.Join(cmd, " ")}
44 proc, err := os.StartProcess("/bin/bash", bashArgs, &os.ProcAttr{
45 Files: []*os.File{inr, outw, outw},
46 Env: append(os.Environ(), env...),
53 return fmt.Errorf("Process start error: " + err.Error())
62 stdoutDone := make(chan struct{})
63 go cmdPumpStdout(so, outr, stdoutDone, sid, cmdID, log, eoCB, data)
65 // Blocking function that poll input or wait for end of process
66 cmdPumpStdin(so, inw, proc, sid, cmdID, cmdExecTimeout, log, eeCB, data)
68 // Some commands will exit when stdin is closed.
73 if status, err := proc.Wait(); err == nil {
74 // Other commands need a bonk on the head.
76 if err := proc.Signal(os.Interrupt); err != nil {
77 log.Errorln("Proc interrupt:", err)
82 case <-time.After(time.Second):
83 // A bigger bonk on the head.
84 if err := proc.Signal(os.Kill); err != nil {
85 log.Errorln("Proc term:", err)
96 func cmdPumpStdin(so *socketio.Socket, w io.Writer, proc *os.Process,
97 sid string, cmdID int, tmo int, log *logrus.Logger, exitFuncCB EmitExitCB,
98 data *map[string]interface{}) {
99 /* XXX - code to add to support stdin through WS
101 _, message, err := so. ?? ReadMessage()
105 message = append(message, '\n')
106 if _, err := w.Write(message); err != nil {
112 // Monitor process exit
113 type DoneChan struct {
117 done := make(chan DoneChan, 1)
120 sts, err := proc.Wait()
122 s := sts.Sys().(syscall.WaitStatus)
123 status = s.ExitStatus()
125 done <- DoneChan{status, err}
131 exitFuncCB(sid, cmdID, dC.status, dC.err, data)
132 case <-time.After(time.Duration(tmo) * time.Second):
133 exitFuncCB(sid, cmdID, -99,
134 fmt.Errorf("Exit Timeout for command ID %v", cmdID), data)
138 func cmdPumpStdout(so *socketio.Socket, r io.Reader, done chan struct{},
139 sid string, cmdID int, log *logrus.Logger, emitFuncCB EmitOutputCB, data *map[string]interface{}) {
143 sc := bufio.NewScanner(r)
145 emitFuncCB(sid, cmdID, string(sc.Bytes()), "", data)
148 log.Errorln("scan:", sc.Err())