12 "github.com/Sirupsen/logrus"
13 "github.com/googollee/go-socket.io"
16 // EmitOutputCB is the function callback used to emit data
17 type EmitOutputCB func(sid string, cmdID int, stdout, stderr string)
19 // EmitExitCB is the function callback used to emit exit proc code
20 type EmitExitCB func(sid string, cmdID int, code int, err error)
23 // https://github.com/gorilla/websocket/blob/master/examples/command/main.go
25 // ExecPipeWs executes a command and redirect stdout/stderr into a WebSocket
26 func ExecPipeWs(cmd string, so *socketio.Socket, sid string, cmdID int,
27 cmdExecTimeout int, log *logrus.Logger, eoCB EmitOutputCB, eeCB EmitExitCB) error {
29 outr, outw, err := os.Pipe()
31 return fmt.Errorf("Pipe stdout error: " + err.Error())
34 // XXX - do we need to pipe stdin one day ?
35 inr, inw, err := os.Pipe()
39 return fmt.Errorf("Pipe stdin error: " + err.Error())
42 bashArgs := []string{"/bin/bash", "-c", cmd}
43 proc, err := os.StartProcess("/bin/bash", bashArgs, &os.ProcAttr{
44 Files: []*os.File{inr, outw, outw},
51 return fmt.Errorf("Process start error: " + err.Error())
60 stdoutDone := make(chan struct{})
61 go cmdPumpStdout(so, outr, stdoutDone, sid, cmdID, log, eoCB)
63 // Blocking function that poll input or wait for end of process
64 cmdPumpStdin(so, inw, proc, sid, cmdID, cmdExecTimeout, log, eeCB)
66 // Some commands will exit when stdin is closed.
71 if status, err := proc.Wait(); err == nil {
72 // Other commands need a bonk on the head.
74 if err := proc.Signal(os.Interrupt); err != nil {
75 log.Errorln("Proc interrupt:", err)
80 case <-time.After(time.Second):
81 // A bigger bonk on the head.
82 if err := proc.Signal(os.Kill); err != nil {
83 log.Errorln("Proc term:", err)
94 func cmdPumpStdin(so *socketio.Socket, w io.Writer, proc *os.Process,
95 sid string, cmdID int, tmo int, log *logrus.Logger, exitFuncCB EmitExitCB) {
96 /* XXX - code to add to support stdin through WS
98 _, message, err := so. ?? ReadMessage()
102 message = append(message, '\n')
103 if _, err := w.Write(message); err != nil {
109 // Monitor process exit
110 type DoneChan struct {
114 done := make(chan DoneChan, 1)
117 sts, err := proc.Wait()
119 s := sts.Sys().(syscall.WaitStatus)
120 status = s.ExitStatus()
122 done <- DoneChan{status, err}
128 exitFuncCB(sid, cmdID, dC.status, dC.err)
129 case <-time.After(time.Duration(tmo) * time.Second):
130 exitFuncCB(sid, cmdID, -99,
131 fmt.Errorf("Exit Timeout for command ID %v", cmdID))
135 func cmdPumpStdout(so *socketio.Socket, r io.Reader, done chan struct{},
136 sid string, cmdID int, log *logrus.Logger, emitFuncCB EmitOutputCB) {
140 sc := bufio.NewScanner(r)
142 emitFuncCB(sid, cmdID, string(sc.Bytes()), "")
145 log.Errorln("scan:", sc.Err())