9 "github.com/gin-gonic/gin"
10 "github.com/iotbzh/xds-server/lib/common"
13 // ExecArgs JSON parameters of /exec command
14 type ExecArgs struct {
16 RPath string `json:"rpath"` // relative path into project
17 Cmd string `json:"cmd" binding:"required"`
18 Args []string `json:"args"`
19 CmdTimeout int `json:"timeout"` // command completion timeout in Second
22 // ExecOutMsg Message send on each output (stdout+stderr) of executed command
23 type ExecOutMsg struct {
24 CmdID string `json:"cmdID"`
25 Timestamp string `json:timestamp`
26 Stdout string `json:"stdout"`
27 Stderr string `json:"stderr"`
30 // ExecExitMsg Message send when executed command exited
31 type ExecExitMsg struct {
32 CmdID string `json:"cmdID"`
33 Timestamp string `json:timestamp`
34 Code int `json:"code"`
35 Error error `json:"error"`
38 // Event name send in WS
39 const ExecOutEvent = "exec:output"
40 const ExecExitEvent = "exec:exit"
44 // ExecCmd executes remotely a command
45 func (s *APIService) execCmd(c *gin.Context) {
47 if c.BindJSON(&args) != nil {
48 common.APIError(c, "Invalid arguments")
52 // TODO: add permission
54 // Retrieve session info
55 sess := s.sessions.Get(c)
57 common.APIError(c, "Unknown sessions")
62 common.APIError(c, "Websocket not established")
66 // Allow to pass id in url (/exec/:id) or as JSON argument
72 common.APIError(c, "Invalid id")
76 prj := s.cfg.GetFolderFromID(id)
78 common.APIError(c, "Unknown id")
82 execTmo := args.CmdTimeout
84 // TODO get default timeout from config.json file
85 execTmo = 24 * 60 * 60 // 1 day
88 // Define callback for output
89 var oCB common.EmitOutputCB
90 oCB = func(sid string, id int, stdout, stderr string) {
91 // IO socket can be nil when disconnected
92 so := s.sessions.IOSocketGet(sid)
94 s.log.Infof("%s not emitted: WS closed - sid: %s - msg id:%d", ExecOutEvent, sid, id)
97 s.log.Debugf("%s emitted - WS sid %s - id:%d", ExecOutEvent, sid, id)
99 // FIXME replace by .BroadcastTo a room
100 err := (*so).Emit(ExecOutEvent, ExecOutMsg{
101 CmdID: strconv.Itoa(id),
102 Timestamp: time.Now().String(),
107 s.log.Errorf("WS Emit : %v", err)
111 // Define callback for output
112 eCB := func(sid string, id int, code int, err error) {
113 s.log.Debugf("Command [Cmd ID %d] exited: code %d, error: %v", id, code, err)
115 // IO socket can be nil when disconnected
116 so := s.sessions.IOSocketGet(sid)
118 s.log.Infof("%s not emitted - WS closed (id:%d", ExecExitEvent, id)
122 // FIXME replace by .BroadcastTo a room
123 e := (*so).Emit(ExecExitEvent, ExecExitMsg{
124 CmdID: strconv.Itoa(id),
125 Timestamp: time.Now().String(),
130 s.log.Errorf("WS Emit : %v", e)
134 cmdID := execCommandID
137 cmd := "cd " + prj.GetFullPath(args.RPath) + " && " + args.Cmd
138 if len(args.Args) > 0 {
139 cmd += " " + strings.Join(args.Args, " ")
142 s.log.Debugf("Execute [Cmd ID %d]: %v %v", cmdID, cmd)
143 err := common.ExecPipeWs(cmd, sop, sess.ID, cmdID, execTmo, s.log, oCB, eCB)
145 common.APIError(c, err.Error())
149 c.JSON(http.StatusOK,