1// Copyright 2017 syzkaller project authors. All rights reserved. 2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4package vmimpl 5 6import ( 7 "fmt" 8 "io" 9 "os/exec" 10 "sync" 11 "syscall" 12 "unsafe" 13 14 "github.com/google/syzkaller/pkg/osutil" 15 "golang.org/x/sys/unix" 16) 17 18// Tested on Suzy-Q and BeagleBone. 19func OpenConsole(con string) (rc io.ReadCloser, err error) { 20 fd, err := syscall.Open(con, syscall.O_RDONLY|syscall.O_NOCTTY|syscall.O_SYNC, 0) 21 if err != nil { 22 return nil, fmt.Errorf("failed to open console file: %v", err) 23 } 24 defer func() { 25 if fd != -1 { 26 syscall.Close(fd) 27 } 28 }() 29 var term unix.Termios 30 _, _, errno := syscall.Syscall(unix.SYS_IOCTL, uintptr(fd), syscallTCGETS, uintptr(unsafe.Pointer(&term))) 31 if errno != 0 { 32 return nil, fmt.Errorf("failed to get console termios: %v", errno) 33 } 34 // no parity bit, only need 1 stop bit, no hardware flowcontrol 35 term.Cflag &^= unixCBAUD | unix.CSIZE | unix.PARENB | unix.CSTOPB | unixCRTSCTS 36 // ignore modem controls 37 term.Cflag |= unix.B115200 | unix.CS8 | unix.CLOCAL | unix.CREAD 38 // setup for non-canonical mode 39 term.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | 40 unix.IGNCR | unix.ICRNL | unix.IXON 41 term.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN 42 term.Oflag &^= unix.OPOST 43 term.Cc[unix.VMIN] = 0 44 term.Cc[unix.VTIME] = 10 // 1 second timeout 45 _, _, errno = syscall.Syscall(unix.SYS_IOCTL, uintptr(fd), syscallTCSETS, uintptr(unsafe.Pointer(&term))) 46 if errno != 0 { 47 return nil, fmt.Errorf("failed to get console termios: %v", errno) 48 } 49 tmp := fd 50 fd = -1 51 return &tty{fd: tmp}, nil 52} 53 54type tty struct { 55 mu sync.Mutex 56 fd int 57} 58 59func (t *tty) Read(buf []byte) (int, error) { 60 t.mu.Lock() 61 defer t.mu.Unlock() 62 if t.fd == -1 { 63 return 0, io.EOF 64 } 65 n, err := syscall.Read(t.fd, buf) 66 if n < 0 { 67 n = 0 68 } 69 return n, err 70} 71 72func (t *tty) Close() error { 73 t.mu.Lock() 74 defer t.mu.Unlock() 75 if t.fd != -1 { 76 syscall.Close(t.fd) 77 t.fd = -1 78 } 79 return nil 80} 81 82// Open dmesg remotely 83func OpenRemoteConsole(bin string, args ...string) (rc io.ReadCloser, err error) { 84 rpipe, wpipe, err := osutil.LongPipe() 85 if err != nil { 86 return nil, err 87 } 88 args = append(args, "dmesg -w") 89 cmd := osutil.Command(bin, args...) 90 cmd.Stdout = wpipe 91 cmd.Stderr = wpipe 92 if err := cmd.Start(); err != nil { 93 rpipe.Close() 94 wpipe.Close() 95 return nil, fmt.Errorf("failed to start adb: %v", err) 96 } 97 wpipe.Close() 98 con := &remoteCon{ 99 cmd: cmd, 100 rpipe: rpipe, 101 } 102 return con, err 103} 104 105// OpenAdbConsole provides fallback console output using 'adb shell dmesg -w'. 106func OpenAdbConsole(bin, dev string) (rc io.ReadCloser, err error) { 107 return OpenRemoteConsole(bin, "-s", dev, "shell") 108} 109 110type remoteCon struct { 111 closeMu sync.Mutex 112 readMu sync.Mutex 113 cmd *exec.Cmd 114 rpipe io.ReadCloser 115} 116 117func (t *remoteCon) Read(buf []byte) (int, error) { 118 t.readMu.Lock() 119 n, err := t.rpipe.Read(buf) 120 t.readMu.Unlock() 121 return n, err 122} 123 124func (t *remoteCon) Close() error { 125 t.closeMu.Lock() 126 cmd := t.cmd 127 t.cmd = nil 128 t.closeMu.Unlock() 129 if cmd == nil { 130 return nil 131 } 132 133 cmd.Process.Kill() 134 135 t.readMu.Lock() 136 t.rpipe.Close() 137 t.readMu.Unlock() 138 139 cmd.Process.Wait() 140 return nil 141} 142