Defined structures for /exec and /signal response.
[src/xds/xds-server.git] / lib / apiv1 / exec.go
index 6300dba..baf431f 100644 (file)
@@ -19,7 +19,8 @@ type (
        // ExecArgs JSON parameters of /exec command
        ExecArgs struct {
                ID              string   `json:"id" binding:"required"`
-               SdkID           string   `json:"sdkid"` // sdk ID to use for setting env
+               SdkID           string   `json:"sdkID"` // sdk ID to use for setting env
+               CmdID           string   `json:"cmdID"` // command unique ID
                Cmd             string   `json:"cmd" binding:"required"`
                Args            []string `json:"args"`
                Env             []string `json:"env"`
@@ -30,6 +31,18 @@ type (
                CmdTimeout      int      `json:"timeout"`         // command completion timeout in Second
        }
 
+       // ExecRes JSON result of /exec command
+       ExecRes struct {
+               Status string `json:"status"` // status OK
+               CmdID  string `json:"cmdID"`  // command unique ID
+       }
+
+       // ExecSigRes JSON result of /signal command
+       ExecSigRes struct {
+               Status string `json:"status"` // status OK
+               CmdID  string `json:"cmdID"`  // command unique ID
+       }
+
        // ExecInMsg Message used to received input characters (stdin)
        ExecInMsg struct {
                CmdID     string `json:"cmdID"`
@@ -104,22 +117,26 @@ func (s *APIService) execCmd(c *gin.Context) {
        }
 
        // Allow to pass id in url (/exec/:id) or as JSON argument
-       id := c.Param("id")
-       if id == "" {
-               id = args.ID
+       idArg := c.Param("id")
+       if idArg == "" {
+               idArg = args.ID
        }
-       if id == "" {
+       if idArg == "" {
                common.APIError(c, "Invalid id")
                return
        }
-
+       id, err := s.mfolders.ResolveID(idArg)
+       if err != nil {
+               common.APIError(c, err.Error())
+               return
+       }
        f := s.mfolders.Get(id)
        if f == nil {
                common.APIError(c, "Unknown id")
                return
        }
-       folder := *f
-       prj := folder.GetConfig()
+       fld := *f
+       prj := fld.GetConfig()
 
        // Build command line
        cmd := []string{}
@@ -135,7 +152,7 @@ func (s *APIService) execCmd(c *gin.Context) {
                }
        }
 
-       cmd = append(cmd, "cd", folder.GetFullPath(args.RPath))
+       cmd = append(cmd, "cd", "\""+fld.GetFullPath(args.RPath)+"\"")
        // FIXME - add 'exec' prevents to use syntax:
        //       xds-exec -l debug -c xds-config.env -- "cd build && cmake .."
        //  but exec is mandatory to allow to pass correctly signals
@@ -145,7 +162,15 @@ func (s *APIService) execCmd(c *gin.Context) {
 
        // Process command arguments
        cmdArgs := make([]string, len(args.Args)+1)
-       copy(cmdArgs, args.Args)
+
+       // Copy and Translate path from client to server
+       for _, aa := range args.Args {
+               if strings.Contains(aa, prj.ClientPath) {
+                       cmdArgs = append(cmdArgs, fld.ConvPathCli2Svr(aa))
+               } else {
+                       cmdArgs = append(cmdArgs, aa)
+               }
+       }
 
        // Allocate pts if tty if used
        if args.TTY {
@@ -160,11 +185,13 @@ func (s *APIService) execCmd(c *gin.Context) {
        }
 
        // Unique ID for each commands
-       cmdID := strconv.Itoa(execCommandID)
-       execCommandID++
+       if args.CmdID == "" {
+               args.CmdID = s.cfg.ServerUID[:18] + "_" + strconv.Itoa(execCommandID)
+               execCommandID++
+       }
 
        // Create new execution over WS context
-       execWS := eows.New(strings.Join(cmd, " "), cmdArgs, sop, sess.ID, cmdID)
+       execWS := eows.New(strings.Join(cmd, " "), cmdArgs, sop, sess.ID, args.CmdID)
        execWS.Log = s.log
 
        // Append client project dir to environment
@@ -193,9 +220,14 @@ func (s *APIService) execCmd(c *gin.Context) {
 
                // Set correct path
                data := e.UserData
-               rootPath := (*data)["RootPath"].(string)
-               clientPath := (*data)["ClientPath"].(string)
-               stdin = strings.Replace(stdin, clientPath, rootPath+"/"+clientPath, -1)
+               prjID := (*data)["ID"].(string)
+               f := s.mfolders.Get(prjID)
+               if f == nil {
+                       s.log.Errorf("InputCB: Cannot get folder ID %s", prjID)
+               } else {
+                       // Translate paths from client to server
+                       stdin = (*f).ConvPathCli2Svr(stdin)
+               }
 
                return stdin, nil
        }
@@ -212,12 +244,16 @@ func (s *APIService) execCmd(c *gin.Context) {
                // Retrieve project ID and RootPath
                data := e.UserData
                prjID := (*data)["ID"].(string)
-               prjRootPath := (*data)["RootPath"].(string)
                gdbServerTTY := (*data)["gdbServerTTY"].(string)
 
-               // Cleanup any references to internal rootpath in stdout & stderr
-               stdout = strings.Replace(stdout, prjRootPath, "", -1)
-               stderr = strings.Replace(stderr, prjRootPath, "", -1)
+               f := s.mfolders.Get(prjID)
+               if f == nil {
+                       s.log.Errorf("OutputCB: Cannot get folder ID %s", prjID)
+               } else {
+                       // Translate paths from server to client
+                       stdout = (*f).ConvPathSvr2Cli(stdout)
+                       stderr = (*f).ConvPathSvr2Cli(stderr)
+               }
 
                s.log.Debugf("%s emitted - WS sid[4:] %s - id:%s - prjID:%s", ExecOutEvent, e.Sid[4:], e.CmdID, prjID)
                if stdout != "" {
@@ -275,6 +311,16 @@ func (s *APIService) execCmd(c *gin.Context) {
        execWS.ExitCB = func(e *eows.ExecOverWS, code int, err error) {
                s.log.Debugf("Command [Cmd ID %s] exited: code %d, error: %v", e.CmdID, code, err)
 
+               // Close client tty
+               defer func() {
+                       if gdbPty != nil {
+                               gdbPty.Close()
+                       }
+                       if gdbTty != nil {
+                               gdbTty.Close()
+                       }
+               }()
+
                // IO socket can be nil when disconnected
                so := s.sessions.IOSocketGet(e.Sid)
                if so == nil {
@@ -305,14 +351,7 @@ func (s *APIService) execCmd(c *gin.Context) {
                                }
                                time.Sleep(time.Second)
                        }
-               }
-
-               // Close client tty
-               if gdbPty != nil {
-                       gdbPty.Close()
-               }
-               if gdbTty != nil {
-                       gdbTty.Close()
+                       s.log.Debugf("OK file are synchronized.")
                }
 
                // FIXME replace by .BroadcastTo a room
@@ -330,8 +369,6 @@ func (s *APIService) execCmd(c *gin.Context) {
        // User data (used within callbacks)
        data := make(map[string]interface{})
        data["ID"] = prj.ID
-       data["RootPath"] = prj.RootPath
-       data["ClientPath"] = prj.ClientPath
        data["ExitImmediate"] = args.ExitImmediate
        if args.TTY && args.TTYGdbserverFix {
                data["gdbServerTTY"] = "workaround"
@@ -341,7 +378,7 @@ func (s *APIService) execCmd(c *gin.Context) {
        execWS.UserData = &data
 
        // Start command execution
-       s.log.Debugf("Execute [Cmd ID %s]: %v %v", execWS.CmdID, execWS.Cmd, execWS.Args)
+       s.log.Infof("Execute [Cmd ID %s]: %v %v", execWS.CmdID, execWS.Cmd, execWS.Args)
 
        err = execWS.Start()
        if err != nil {
@@ -349,11 +386,7 @@ func (s *APIService) execCmd(c *gin.Context) {
                return
        }
 
-       c.JSON(http.StatusOK,
-               gin.H{
-                       "status": "OK",
-                       "cmdID":  execWS.CmdID,
-               })
+       c.JSON(http.StatusOK, ExecRes{Status: "OK", CmdID: execWS.CmdID})
 }
 
 // ExecCmd executes remotely a command
@@ -379,8 +412,5 @@ func (s *APIService) execSignalCmd(c *gin.Context) {
                return
        }
 
-       c.JSON(http.StatusOK,
-               gin.H{
-                       "status": "OK",
-               })
+       c.JSON(http.StatusOK, ExecSigRes{Status: "OK", CmdID: args.CmdID})
 }