diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2016-12-08 20:54:09 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2016-12-19 15:56:10 +0100 |
| commit | a074da17a4055352fea94afbd5a15c53d0946653 (patch) | |
| tree | 8c53251fdfe95dab163fa38feebf2a9406be5664 /vm/adb | |
| parent | 53366f457199a1a869ceeb461d637e1a33dc21d1 (diff) | |
vm/adb: support BeagleBone console
BeagleBone console requires some special tty-ism to work.
Fortunately, this code also works with Suzy-Q.
Diffstat (limited to 'vm/adb')
| -rw-r--r-- | vm/adb/adb.go | 73 | ||||
| -rw-r--r-- | vm/adb/console.go | 75 |
2 files changed, 92 insertions, 56 deletions
diff --git a/vm/adb/adb.go b/vm/adb/adb.go index f16a01e0e..77f7489c9 100644 --- a/vm/adb/adb.go +++ b/vm/adb/adb.go @@ -47,7 +47,7 @@ func ctor(cfg *vm.Config) (vm.Instance, error) { return nil, err } var err error - if inst.console, err = findConsole(inst.cfg.Device); err != nil { + if inst.console, err = findConsole(inst.cfg.Bin, inst.cfg.Device); err != nil { return nil, err } if err := inst.checkBatteryLevel(); err != nil { @@ -83,7 +83,7 @@ var ( // while Suzy-Q console uses the same USB calbe as adb. // The overall idea is as follows. We use 'adb shell' to write a unique string onto console, // then we read from all console devices and see on what console the unique string appears. -func findConsole(dev string) (string, error) { +func findConsole(adb, dev string) (string, error) { consoleCacheMu.Lock() defer consoleCacheMu.Unlock() if con := devToConsole[dev]; con != "" { @@ -103,20 +103,17 @@ func findConsole(dev string) (string, error) { out := new([]byte) output[con] = out go func(con string) { - cmd := exec.Command("cat", con) - stdout, err := cmd.StdoutPipe() + tty, err := openConsole(con) if err != nil { errors <- err + return } - if cmd.Start() != nil { - errors <- err - } + defer tty.Close() go func() { <-done - cmd.Process.Kill() + tty.Close() }() - *out, _ = ioutil.ReadAll(stdout) - cmd.Wait() + *out, _ = ioutil.ReadAll(tty) errors <- nil }(con) } @@ -125,7 +122,7 @@ func findConsole(dev string) (string, error) { } time.Sleep(500 * time.Millisecond) unique := fmt.Sprintf(">>>%v<<<", dev) - cmd := exec.Command("adb", "-s", dev, "shell", "echo", "\"", unique, "\"", ">", "/dev/kmsg") + cmd := exec.Command(adb, "-s", dev, "shell", "echo", "\"", unique, "\"", ">", "/dev/kmsg") if out, err := cmd.CombinedOutput(); err != nil { return "", fmt.Errorf("failed to run adb shell: %v\n%s", err, out) } @@ -329,34 +326,14 @@ func (inst *instance) Copy(hostSrc string) (string, error) { } func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) (<-chan []byte, <-chan error, error) { - catRpipe, catWpipe, err := vm.LongPipe() + tty, err := openConsole(inst.console) if err != nil { return nil, nil, err } - cat := exec.Command("cat", inst.console) - cat.Stdout = catWpipe - cat.Stderr = catWpipe - if err := cat.Start(); err != nil { - catRpipe.Close() - catWpipe.Close() - return nil, nil, fmt.Errorf("failed to start cat %v: %v", inst.console, err) - - } - catWpipe.Close() - catDone := make(chan error, 1) - go func() { - err := cat.Wait() - if inst.cfg.Debug { - Logf(0, "cat exited: %v", err) - } - catDone <- fmt.Errorf("cat exited: %v", err) - }() - adbRpipe, adbWpipe, err := vm.LongPipe() if err != nil { - cat.Process.Kill() - catRpipe.Close() + tty.Close() return nil, nil, err } if inst.cfg.Debug { @@ -366,29 +343,20 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin adb.Stdout = adbWpipe adb.Stderr = adbWpipe if err := adb.Start(); err != nil { - cat.Process.Kill() - catRpipe.Close() + tty.Close() adbRpipe.Close() adbWpipe.Close() return nil, nil, fmt.Errorf("failed to start adb: %v", err) } adbWpipe.Close() - adbDone := make(chan error, 1) - go func() { - err := adb.Wait() - if inst.cfg.Debug { - Logf(0, "adb exited: %v", err) - } - adbDone <- fmt.Errorf("adb exited: %v", err) - }() var tee io.Writer if inst.cfg.Debug { tee = os.Stdout } merger := vm.NewOutputMerger(tee) - merger.Add(catRpipe) - merger.Add(adbRpipe) + merger.Add("console", tty) + merger.Add("adb", adbRpipe) errc := make(chan error, 1) signal := func(err error) { @@ -402,27 +370,20 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin select { case <-time.After(timeout): signal(vm.TimeoutErr) - cat.Process.Kill() - adb.Process.Kill() case <-stop: signal(vm.TimeoutErr) - cat.Process.Kill() - adb.Process.Kill() case <-inst.closed: if inst.cfg.Debug { Logf(0, "instance closed") } signal(fmt.Errorf("instance closed")) - cat.Process.Kill() - adb.Process.Kill() - case err := <-catDone: - signal(err) - adb.Process.Kill() - case err := <-adbDone: + case err := <-merger.Err: signal(err) - cat.Process.Kill() } + tty.Close() + adb.Process.Kill() merger.Wait() + adb.Wait() }() return merger.Output, errc, nil } diff --git a/vm/adb/console.go b/vm/adb/console.go new file mode 100644 index 000000000..e55e019ae --- /dev/null +++ b/vm/adb/console.go @@ -0,0 +1,75 @@ +// Copyright 2016 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. + +package adb + +import ( + "fmt" + "io" + "sync" + "syscall" + "unsafe" + + . "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 Termios + if _, _, errno := syscall.Syscall(SYS_IOCTL, uintptr(fd), 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 &^= CBAUD | CSIZE | PARENB | CSTOPB | CRTSCTS + // ignore modem controls + term.Cflag |= B115200 | CS8 | CLOCAL | CREAD + // setup for non-canonical mode + term.Iflag &^= IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON + term.Lflag &^= ECHO | ECHONL | ICANON | ISIG | IEXTEN + term.Oflag &^= OPOST + term.Cc[VMIN] = 0 + term.Cc[VTIME] = 10 // 1 second timeout + if _, _, errno := syscall.Syscall(SYS_IOCTL, uintptr(fd), 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 +} |
