Пример #1
0
func handleConnection(c net.Conn) {
	defer c.Close()

	// Start the command
	cmd := exec.Command(flagCommand)

	// Create PTY
	pty, tty, err := pty.Open()
	if err != nil {
		log.Printf("error: could not open PTY: %s", err)
		return
	}
	defer tty.Close()
	defer pty.Close()

	// Put the TTY into raw mode
	_, err = terminal.MakeRaw(int(tty.Fd()))
	if err != nil {
		log.Printf("warn: could not make TTY raw: %s", err)
	}

	// Hook everything up
	cmd.Stdout = tty
	cmd.Stdin = tty
	cmd.Stderr = tty
	if cmd.SysProcAttr == nil {
		cmd.SysProcAttr = &syscall.SysProcAttr{}
	}

	cmd.SysProcAttr.Setctty = true
	cmd.SysProcAttr.Setsid = true

	// Start command
	err = cmd.Start()
	if err != nil {
		log.Printf("error: could not start command: %s", err)
		return
	}

	errs := make(chan error, 3)

	go func() {
		_, err := io.Copy(c, pty)
		errs <- err
	}()
	go func() {
		_, err := io.Copy(pty, c)
		errs <- err
	}()
	go func() {
		errs <- cmd.Wait()
	}()

	// Wait for a single error, then shut everything down. Since returning from
	// this function closes the connection, we just read a single error and
	// then continue.
	<-errs
	log.Printf("info: connection from %s finished", c.RemoteAddr())
}
Пример #2
0
func HandleSocket(w http.ResponseWriter, r *http.Request, ptyFunc func(string) *Pty) {
	websocket.Handler(func(conn *websocket.Conn) {
		var obj termMsg
		dec := json.NewDecoder(conn)
		err := dec.Decode(&obj)
		if err != nil {
			log.Println("hterm:", err)
			return
		}
		if obj.Args == nil || obj.Width == nil || obj.Height == nil {
			log.Println("hterm: no args")
			return
		}
		pty := ptyFunc(*obj.Args)
		pty.Size(*obj.Width, *obj.Height)
		go io.Copy(conn, pty)
		for {
			var obj termMsg
			err := dec.Decode(&obj)
			if err != nil {
				log.Println("hterm:", err)
				if err == io.EOF {
					log.Println("hterm: sending SIGHUP to process")
					pidForKill := fmt.Sprintf("-%d", pty.process.Pid)
					cmd := exec.Command("kill", "-1", pidForKill)
					cmd.Start()
					cmd.Wait()
					pty.Close()

					log.Println("hterm: waiting for exit")
					processState, err := pty.process.Wait()
					log.Println("hterm:", processState, err)
				}
				break
			}

			if obj.Width != nil && obj.Height != nil {
				pty.Size(*obj.Width, *obj.Height)
				continue
			}
			if obj.Data != nil {
				_, err = io.WriteString(pty, *obj.Data)
				if err != nil {
					log.Println("hterm:", err)
					break
				}
			}
		}
	}).ServeHTTP(w, r)
}
Пример #3
0
		flyCmd := exec.Command(flyPath, append([]string{"-t", atcServer.URL()}, commandWithArgs...)...)
		flyCmd.Stdin = tty

		sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter)
		Ω(err).ShouldNot(HaveOccurred())

		Eventually(hijacked).Should(BeClosed())

		_, err = pty.WriteString("some stdin")
		Ω(err).ShouldNot(HaveOccurred())

		Eventually(sess.Out).Should(gbytes.Say("some stdout"))
		Eventually(sess.Err).Should(gbytes.Say("some stderr"))

		err = pty.Close()
		Ω(err).ShouldNot(HaveOccurred())

		<-sess.Exited
		Ω(sess.ExitCode()).Should(Equal(123))
	}

	hijack := func(args ...string) {
		fly("hijack", args...)
	}

	Context("with no arguments", func() {
		BeforeEach(func() {
			didHijack := make(chan struct{})
			hijacked = didHijack
Пример #4
0
func (d *Daemon) serveAttach(w http.ResponseWriter, r *http.Request) {
	Debugf("responding to attach")

	name := r.FormValue("name")
	if name == "" {
		fmt.Fprintf(w, "failed parsing name")
		return
	}

	command := r.FormValue("command")
	if command == "" {
		fmt.Fprintf(w, "failed parsing command")
		return
	}

	secret := r.FormValue("secret")
	if secret == "" {
		fmt.Fprintf(w, "failed parsing secret")
		return
	}

	addr := ":0"
	// tcp6 doesn't seem to work with Dial("tcp", ) at the client
	l, err := net.Listen("tcp4", addr)
	if err != nil {
		fmt.Fprintf(w, "failed listening")
		return
	}
	fmt.Fprintf(w, "%s", l.Addr().String())

	go func(l net.Listener, name string, command string, secret string) {
		conn, err := l.Accept()
		l.Close()
		if err != nil {
			Debugf(err.Error())
			return
		}
		defer conn.Close()

		// FIXME(niemeyer): This likely works okay because the kernel tends to
		// be sane enough to not break down such a small amount of data into
		// multiple operations. That said, if we were to make it work
		// independent of the good will of the kernel and network layers, we'd
		// have to take into account that Read might also return a single byte,
		// for example, and then return more when it was next called. Or, it
		// might return a password plus more data that the client delivered
		// anticipating it would have a successful authentication.
		//
		// We could easily handle it using buffered io (bufio package), but that
		// would spoil the use of conn directly below when binding it to
		// the pty. So, given it's a trivial amount of data, I suggest calling
		// a local helper function that will read byte by byte until it finds
		// a predefined delimiter ('\n'?) and returns (data string, err error).
		//
		b := make([]byte, 100)
		n, err := conn.Read(b)
		if err != nil {
			Debugf("bad read: %s", err.Error())
			return
		}
		if n != len(secret) {
			Debugf("read %d characters, secret is %d", n, len(secret))
			return
		}
		if string(b[:n]) != secret {
			Debugf("Wrong secret received from attach client")
			return
		}
		Debugf("Attaching")

		c, err := lxc.NewContainer(name, d.lxcpath)
		if err != nil {
			Debugf("%s", err.Error())
		}

		pty, tty, err := pty.Open()

		if err != nil {
			Debugf("Failed opening getting a tty: %q", err.Error())
			return
		}

		defer pty.Close()
		defer tty.Close()

		/*
		 * The pty will be passed to the container's Attach.  The two
		 * below goroutines will copy output from the socket to the
		 * pty.stdin, and from pty.std{out,err} to the socket
		 * If the RunCommand exits, we want ourselves (the gofunc) and
		 * the copy-goroutines to exit.  If the connection closes, we
		 * also want to exit
		 */
		go func() {
			io.Copy(pty, conn)
			Debugf("conn->pty exiting")
			return
		}()
		go func() {
			io.Copy(conn, pty)
			Debugf("pty->conn exiting")
			return
		}()

		options := lxc.DefaultAttachOptions

		options.StdinFd = tty.Fd()
		options.StdoutFd = tty.Fd()
		options.StderrFd = tty.Fd()

		options.ClearEnv = true

		_, err = c.RunCommand([]string{command}, options)
		if err != nil {
			return
		}

		Debugf("RunCommand exited, stopping console")
	}(l, name, command, secret)
}