From af643baa328ae3d4b7076054bba648c4b8bf8056 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Fri, 2 Jun 2017 20:09:00 +0200 Subject: vm: overhaul VM infrastructure currently has several problems: - Config struct is complete mess with a superset of params for all VM types - verification of Config is mess spread across several places - there is no place where VM code could do global initialization like creating GCE connection, uploading GCE image to GCS, matching adb devices with consoles, etc - it hard to add private VM implementations such impl would need to add code to config package which would lead to constant merge conflicts - interface for VM implementation is mixed with interface for VM users this does not allow to provide best interface for both of them - there is no way to add common code for all VM implementations This change solves these problems by: - splitting VM interface for users (vm package) and VM interface for VM implementations (vmimpl pacakge), this in turn allows to add common code - adding Pool concept that allows to do global initialization and config checking at the right time - decoupling manager config from VM-specific config each VM type now defines own config Note: manager configs need to be changed after this change: VM-specific parts are moved to own "vm" subobject. Note: this change also drops "local" VM type. Its story was long unclear and there is now syz-stress which solves the same problem. --- vm/vmimpl/console.go | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 vm/vmimpl/console.go (limited to 'vm/vmimpl/console.go') diff --git a/vm/vmimpl/console.go b/vm/vmimpl/console.go new file mode 100644 index 000000000..f4a0b71d1 --- /dev/null +++ b/vm/vmimpl/console.go @@ -0,0 +1,134 @@ +// Copyright 2017 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +// +build !ppc64le + +package vmimpl + +import ( + "fmt" + "io" + "os/exec" + "sync" + "syscall" + "unsafe" + + "github.com/google/syzkaller/pkg/osutil" + "golang.org/x/sys/unix" +) + +// Tested on Suzy-Q and BeagleBone. +func OpenConsole(con string) (rc io.ReadCloser, err error) { + fd, err := syscall.Open(con, syscall.O_RDONLY|syscall.O_NOCTTY|syscall.O_SYNC, 0) + if err != nil { + return nil, fmt.Errorf("failed to open console file: %v", err) + } + defer func() { + if fd != -1 { + syscall.Close(fd) + } + }() + var term unix.Termios + if _, _, errno := syscall.Syscall(unix.SYS_IOCTL, uintptr(fd), unix.TCGETS2, uintptr(unsafe.Pointer(&term))); errno != 0 { + return nil, fmt.Errorf("failed to get console termios: %v", errno) + } + // no parity bit, only need 1 stop bit, no hardware flowcontrol + term.Cflag &^= unix.CBAUD | unix.CSIZE | unix.PARENB | unix.CSTOPB | unix.CRTSCTS + // ignore modem controls + term.Cflag |= unix.B115200 | unix.CS8 | unix.CLOCAL | unix.CREAD + // setup for non-canonical mode + term.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON + term.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN + term.Oflag &^= unix.OPOST + term.Cc[unix.VMIN] = 0 + term.Cc[unix.VTIME] = 10 // 1 second timeout + if _, _, errno := syscall.Syscall(unix.SYS_IOCTL, uintptr(fd), unix.TCSETS2, uintptr(unsafe.Pointer(&term))); errno != 0 { + return nil, fmt.Errorf("failed to get console termios: %v", errno) + } + tmp := fd + fd = -1 + return &tty{fd: tmp}, nil +} + +type tty struct { + mu sync.Mutex + fd int +} + +func (t *tty) Read(buf []byte) (int, error) { + t.mu.Lock() + defer t.mu.Unlock() + if t.fd == -1 { + return 0, io.EOF + } + n, err := syscall.Read(t.fd, buf) + if n < 0 { + n = 0 + } + return n, err +} + +func (t *tty) Close() error { + t.mu.Lock() + defer t.mu.Unlock() + if t.fd != -1 { + syscall.Close(t.fd) + t.fd = -1 + } + return nil +} + +// OpenAdbConsole provides fallback console output using 'adb shell dmesg -w'. +func OpenAdbConsole(bin, dev string) (rc io.ReadCloser, err error) { + rpipe, wpipe, err := osutil.LongPipe() + if err != nil { + return nil, err + } + cmd := exec.Command(bin, "-s", dev, "shell", "dmesg -w") + cmd.Stdout = wpipe + cmd.Stderr = wpipe + if err := cmd.Start(); err != nil { + rpipe.Close() + wpipe.Close() + return nil, fmt.Errorf("failed to start adb: %v", err) + } + wpipe.Close() + con := &adbCon{ + cmd: cmd, + rpipe: rpipe, + } + return con, err +} + +type adbCon struct { + closeMu sync.Mutex + readMu sync.Mutex + cmd *exec.Cmd + rpipe io.ReadCloser +} + +func (t *adbCon) Read(buf []byte) (int, error) { + t.readMu.Lock() + n, err := t.rpipe.Read(buf) + t.readMu.Unlock() + return n, err +} + +func (t *adbCon) Close() error { + t.closeMu.Lock() + cmd := t.cmd + t.cmd = nil + t.closeMu.Unlock() + if cmd == nil { + return nil + } + + cmd.Process.Kill() + + t.readMu.Lock() + t.rpipe.Close() + t.readMu.Unlock() + + cmd.Process.Wait() + return nil +} -- cgit mrf-deployment