예제 #1
0
파일: exec.go 프로젝트: rockstar/lxd
func sendTermSize(control *websocket.Conn) error {
	width, height, err := terminal.GetSize(int(syscall.Stdout))
	if err != nil {
		return err
	}

	shared.Debugf("Window size is now: %dx%d", width, height)

	w, err := control.NextWriter(websocket.TextMessage)
	if err != nil {
		return err
	}

	msg := shared.ContainerExecControl{}
	msg.Command = "window-resize"
	msg.Args = make(map[string]string)
	msg.Args["width"] = strconv.Itoa(width)
	msg.Args["height"] = strconv.Itoa(height)

	buf, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	_, err = w.Write(buf)

	w.Close()
	return err
}
예제 #2
0
파일: client.go 프로젝트: achanda/lxd
func (c *Client) Exec(name string, cmd []string, env map[string]string, stdin *os.File, stdout *os.File, stderr *os.File) (int, error) {
	interactive := terminal.IsTerminal(int(stdin.Fd()))

	body := shared.Jmap{"command": cmd, "wait-for-websocket": true, "interactive": interactive, "environment": env}

	resp, err := c.post(fmt.Sprintf("containers/%s/exec", name), body, Async)
	if err != nil {
		return -1, err
	}

	md := execMd{}
	if err := json.Unmarshal(resp.Metadata, &md); err != nil {
		return -1, err
	}

	if interactive {
		if wsControl, ok := md.FDs["control"]; ok {
			go func() {
				control, err := c.websocket(resp.Operation, wsControl)
				if err != nil {
					return
				}

				for {
					width, height, err := terminal.GetSize(syscall.Stdout)
					if err != nil {
						continue
					}

					shared.Debugf("Window size is now: %dx%d", width, height)

					w, err := control.NextWriter(websocket.TextMessage)
					if err != nil {
						shared.Debugf("Got error getting next writer %s", err)
						break
					}

					msg := shared.ContainerExecControl{}
					msg.Command = "window-resize"
					msg.Args = make(map[string]string)
					msg.Args["width"] = strconv.Itoa(width)
					msg.Args["height"] = strconv.Itoa(height)

					buf, err := json.Marshal(msg)
					if err != nil {
						shared.Debugf("Failed to convert to json %s", err)
						break
					}
					_, err = w.Write(buf)

					w.Close()
					if err != nil {
						shared.Debugf("Got err writing %s", err)
						break
					}

					ch := make(chan os.Signal)
					signal.Notify(ch, syscall.SIGWINCH)
					sig := <-ch

					shared.Debugf("Received '%s signal', updating window geometry.", sig)
				}

				closeMsg := websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")
				control.WriteMessage(websocket.CloseMessage, closeMsg)
			}()
		}

		conn, err := c.websocket(resp.Operation, md.FDs["0"])
		if err != nil {
			return -1, err
		}
		shared.WebsocketSendStream(conn, stdin)
		<-shared.WebsocketRecvStream(stdout, conn)
	} else {
		sources := []*os.File{stdin, stdout, stderr}
		conns := make([]*websocket.Conn, 3)
		dones := make([]chan bool, 3)
		for i := 0; i < 3; i++ {
			conns[i], err = c.websocket(resp.Operation, md.FDs[strconv.Itoa(i)])
			if err != nil {
				return -1, err
			}

			if i == 0 {
				dones[i] = shared.WebsocketSendStream(conns[i], sources[i])
			} else {
				dones[i] = shared.WebsocketRecvStream(sources[i], conns[i])
			}
		}

		/*
		 * We'll get a read signal from each of stdout, stderr when they've
		 * both died. We need to wait for these in addition to the operation,
		 * because the server may indicate that the operation is done before we
		 * can actually read the last bits of data off these sockets and print
		 * it to the screen.
		 *
		 * We don't wait for stdin here, because if we're interactive, the user
		 * may not have closed it (e.g. if the command exits but the user
		 * didn't ^D).
		 */
		for i := 1; i < 3; i++ {
			<-dones[i]
		}

		// Once we're done, we explicitly close stdin, to signal the websockets
		// we're done.
		sources[0].Close()
	}

	// Now, get the operation's status too.
	op, err := c.WaitFor(resp.Operation)
	if err != nil {
		return -1, err
	}

	if op.StatusCode == shared.Failure {
		return -1, op.GetError()
	}

	if op.StatusCode != shared.Success {
		return -1, fmt.Errorf(gettext.Gettext("got bad op status %s"), op.Status)
	}

	opMd, err := op.MetadataAsMap()
	if err != nil {
		return -1, err
	}

	return opMd.GetInt("return")
}