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()) }
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) }
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
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) }