diff --git a/vendor.conf b/vendor.conf index 80e8627d4..3f331692f 100644 --- a/vendor.conf +++ b/vendor.conf @@ -5,7 +5,7 @@ github.com/docker/infrakit cb420e3e50ea60afe58538b1d3cab1cb14059433 github.com/golang/protobuf c9c7427a2a70d2eb3bafa0ab2dc163e45f143317 github.com/googleapis/gax-go 8c5154c0fe5bf18cf649634d4c6df50897a32751 github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062 -github.com/moby/hyperkit 70205a6d5143340299a679af259f70dfcd7cf8a4 +github.com/moby/hyperkit ffbde436bf43219808ebe24dc8f6aacdb0ab57bd github.com/packethost/packngo 91d54000aa56874149d348a884ba083c41d38091 github.com/rneugeba/iso9660wrap 4606f848a055435cdef85305960b0e1bb788d506 github.com/surma/gocpio fcb68777e7dc4ea43ffce871b552c0d073c17495 diff --git a/vendor/github.com/moby/hyperkit/go/hyperkit.go b/vendor/github.com/moby/hyperkit/go/hyperkit.go index 1b67e0046..9fd8ea64f 100644 --- a/vendor/github.com/moby/hyperkit/go/hyperkit.go +++ b/vendor/github.com/moby/hyperkit/go/hyperkit.go @@ -35,6 +35,7 @@ import ( "path/filepath" "strconv" "strings" + "time" "github.com/mitchellh/go-ps" ) @@ -212,6 +213,9 @@ func (h *HyperKit) execute(cmdline string) error { if h.Console == ConsoleFile && h.StateDir == "" { return fmt.Errorf("If ConsoleFile is set, StateDir must be specified") } + if h.Console == ConsoleStdio && !isTerminal(os.Stdout) && h.StateDir == "" { + return fmt.Errorf("If ConsoleStdio is set but stdio is not a terminal, StateDir must be specified") + } if h.ISOImage != "" { if _, err = os.Stat(h.ISOImage); os.IsNotExist(err) { return fmt.Errorf("ISO %s does not exist", h.ISOImage) @@ -420,10 +424,10 @@ func (h *HyperKit) buildArgs(cmdline string) { nextSlot++ } - if h.Console == ConsoleFile { - a = append(a, "-l", fmt.Sprintf("com1,autopty=%s/tty,log=%s/console-ring", h.StateDir, h.StateDir)) - } else { + if h.Console == ConsoleStdio && isTerminal(os.Stdout) { a = append(a, "-l", "com1,stdio") + } else if h.StateDir != "" { + a = append(a, "-l", fmt.Sprintf("com1,autopty=%s/tty,log=%s/console-ring", h.StateDir, h.StateDir)) } kernArgs := fmt.Sprintf("kexec,%s,%s,earlyprintk=serial %s", h.Kernel, h.Initrd, cmdline) @@ -439,14 +443,43 @@ func (h *HyperKit) execHyperKit() error { cmd := exec.Command(h.HyperKit, h.Arguments...) cmd.Env = os.Environ() - // Plumb in stdin/stdout/stderr. If ConsoleStdio is configured - // plumb them to the system streams. If a logger is specified, - // use it for stdout/stderr logging. Otherwise use the default - // /dev/null. + // Plumb in stdin/stdout/stderr. + // + // If ConsoleStdio is configured and we are on a terminal, + // just plugin stdio. If we are not on a terminal we have a + // StateDir (as per checks above) and have configured HyperKit + // to use a PTY in the statedir. In this case, we just open + // the PTY slave and copy it to stdout (and ignore + // stdin). This allows for redirecting of the VM output, used + // for testing. + // + // If a logger is specified, use it for stdout/stderr + // logging. Otherwise use the default /dev/null. if h.Console == ConsoleStdio { - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + if isTerminal(os.Stdout) { + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } else { + go func() { + ttyPath := fmt.Sprintf("%s/tty", h.StateDir) + var tty *os.File + var err error + for { + tty, err = os.OpenFile(ttyPath, os.O_RDONLY, 0) + if err != nil { + time.Sleep(10 * 1000 * 1000 * time.Nanosecond) + continue + } else { + break + } + } + saneTerminal(tty) + setRaw(tty) + io.Copy(os.Stdout, tty) + tty.Close() + }() + } } else if h.log != nil { stdoutChan := make(chan string) stderrChan := make(chan string) diff --git a/vendor/github.com/moby/hyperkit/go/pty_util_darwin.go b/vendor/github.com/moby/hyperkit/go/pty_util_darwin.go new file mode 100644 index 000000000..559e865c8 --- /dev/null +++ b/vendor/github.com/moby/hyperkit/go/pty_util_darwin.go @@ -0,0 +1,70 @@ +package hyperkit + +/* +Most of this code was copied and adjusted from: +https://github.com/containerd/console +which is under Apache License Version 2.0, January 2004 +*/ +import ( + "os" + "unsafe" + + "golang.org/x/sys/unix" +) + +func tcget(fd uintptr, p *unix.Termios) error { + return ioctl(fd, unix.TIOCGETA, uintptr(unsafe.Pointer(p))) +} + +func tcset(fd uintptr, p *unix.Termios) error { + return ioctl(fd, unix.TIOCSETA, uintptr(unsafe.Pointer(p))) +} + +func ioctl(fd, flag, data uintptr) error { + if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, flag, data); err != 0 { + return err + } + return nil +} + +func saneTerminal(f *os.File) error { + // Go doesn't have a wrapper for any of the termios ioctls. + var termios unix.Termios + if err := tcget(f.Fd(), &termios); err != nil { + return err + } + // Set -onlcr so we don't have to deal with \r. + termios.Oflag &^= unix.ONLCR + return tcset(f.Fd(), &termios) +} + +func setRaw(f *os.File) error { + var termios unix.Termios + if err := tcget(f.Fd(), &termios); err != nil { + return err + } + termios = cfmakeraw(termios) + termios.Oflag = termios.Oflag | unix.OPOST + return tcset(f.Fd(), &termios) +} + +// isTerminal checks if the provided file is a terminal +func isTerminal(f *os.File) bool { + var termios unix.Termios + if tcget(f.Fd(), &termios) != nil { + return false + } + return true +} + +func cfmakeraw(t unix.Termios) unix.Termios { + t.Iflag = uint64(uint32(t.Iflag) & ^uint32((unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON))) + t.Oflag = uint64(uint32(t.Oflag) & ^uint32(unix.OPOST)) + t.Lflag = uint64(uint32(t.Lflag) & ^(uint32(unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN))) + t.Cflag = uint64(uint32(t.Cflag) & ^(uint32(unix.CSIZE | unix.PARENB))) + t.Cflag = t.Cflag | unix.CS8 + t.Cc[unix.VMIN] = 1 + t.Cc[unix.VTIME] = 0 + + return t +} diff --git a/vendor/github.com/moby/hyperkit/go/pty_util_fallback.go b/vendor/github.com/moby/hyperkit/go/pty_util_fallback.go new file mode 100644 index 000000000..aff274cc5 --- /dev/null +++ b/vendor/github.com/moby/hyperkit/go/pty_util_fallback.go @@ -0,0 +1,24 @@ +// +build !darwin + +package hyperkit + +import ( + "log" + "os" +) + +func saneTerminal(f *os.File) error { + log.Fatal("Function not supported on your OS") + return nil +} + +func setRaw(f *os.File) error { + log.Fatal("Function not supported on your OS") + return nil +} + +// isTerminal checks if the provided file is a terminal +func isTerminal(f *os.File) bool { + log.Fatal("Function not supported on your OS") + return false +} diff --git a/vendor/github.com/moby/hyperkit/go/vendor.conf b/vendor/github.com/moby/hyperkit/go/vendor.conf index 0e9e15ee7..7ae581711 100644 --- a/vendor/github.com/moby/hyperkit/go/vendor.conf +++ b/vendor/github.com/moby/hyperkit/go/vendor.conf @@ -1 +1,2 @@ github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062 +golang.org/x/sys b90f89a1e7a9c1f6b918820b3daa7f08488c8594