github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/vm/vmimpl/console.go (about) 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 //go:build !windows 4 5 package vmimpl 6 7 import ( 8 "fmt" 9 "io" 10 "os/exec" 11 "sync" 12 "syscall" 13 14 "github.com/google/syzkaller/pkg/osutil" 15 "golang.org/x/sys/unix" 16 ) 17 18 // Tested on Suzy-Q and BeagleBone. 19 func 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: %w", err) 23 } 24 defer func() { 25 if fd != -1 { 26 syscall.Close(fd) 27 } 28 }() 29 term, err := unix.IoctlGetTermios(fd, syscallTCGETS) 30 if err != nil { 31 return nil, fmt.Errorf("failed to get console termios: %w", err) 32 } 33 // No parity bit, only need 1 stop bit, no hardware flowcontrol, 34 term.Cflag &^= unixCBAUD | unix.CSIZE | unix.PARENB | unix.CSTOPB | unixCRTSCTS 35 // Ignore modem controls. 36 term.Cflag |= unix.B115200 | unix.CS8 | unix.CLOCAL | unix.CREAD 37 // Setup for non-canonical mode. 38 term.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | 39 unix.IGNCR | unix.ICRNL | unix.IXON 40 term.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN 41 term.Oflag &^= unix.OPOST 42 term.Cc[unix.VMIN] = 0 43 term.Cc[unix.VTIME] = 10 // 1 second timeout 44 if err = unix.IoctlSetTermios(fd, syscallTCSETS, term); err != nil { 45 return nil, fmt.Errorf("failed to get console termios: %w", err) 46 } 47 tmp := fd 48 fd = -1 49 return &tty{fd: tmp}, nil 50 } 51 52 type tty struct { 53 mu sync.Mutex 54 fd int 55 } 56 57 func (t *tty) Read(buf []byte) (int, error) { 58 t.mu.Lock() 59 defer t.mu.Unlock() 60 if t.fd == -1 { 61 return 0, io.EOF 62 } 63 n, err := syscall.Read(t.fd, buf) 64 if n < 0 { 65 n = 0 66 } 67 return n, err 68 } 69 70 func (t *tty) Close() error { 71 t.mu.Lock() 72 defer t.mu.Unlock() 73 if t.fd != -1 { 74 syscall.Close(t.fd) 75 t.fd = -1 76 } 77 return nil 78 } 79 80 // OpenRemoteKernelLog accesses to the host where Android VM runs on, not Android VM itself. 81 // The host stores all kernel outputs of Android VM so in case of crashes nothing will be lost. 82 func OpenRemoteKernelLog(ip, console string) (rc io.ReadCloser, err error) { 83 rpipe, wpipe, err := osutil.LongPipe() 84 if err != nil { 85 return nil, err 86 } 87 conAddr := "vsoc-01@" + ip 88 cmd := osutil.Command("ssh", conAddr, "tail", "-f", console) 89 cmd.Stdout = wpipe 90 cmd.Stderr = wpipe 91 if _, err := cmd.StdinPipe(); err != nil { 92 rpipe.Close() 93 wpipe.Close() 94 return nil, err 95 } 96 if err := cmd.Start(); err != nil { 97 rpipe.Close() 98 wpipe.Close() 99 return nil, fmt.Errorf("failed to connect to console server: %w", err) 100 } 101 wpipe.Close() 102 con := &remoteCon{ 103 cmd: cmd, 104 rpipe: rpipe, 105 } 106 return con, nil 107 } 108 109 // Open dmesg remotely. 110 func OpenRemoteConsole(bin string, args ...string) (rc io.ReadCloser, err error) { 111 rpipe, wpipe, err := osutil.LongPipe() 112 if err != nil { 113 return nil, err 114 } 115 args = append(args, "dmesg -w") 116 cmd := osutil.Command(bin, args...) 117 cmd.Stdout = wpipe 118 cmd.Stderr = wpipe 119 if err := cmd.Start(); err != nil { 120 rpipe.Close() 121 wpipe.Close() 122 return nil, fmt.Errorf("failed to start adb: %w", err) 123 } 124 wpipe.Close() 125 con := &remoteCon{ 126 cmd: cmd, 127 rpipe: rpipe, 128 } 129 return con, err 130 } 131 132 // OpenAdbConsole provides fallback console output using 'adb shell dmesg -w'. 133 func OpenAdbConsole(bin, dev string) (rc io.ReadCloser, err error) { 134 return OpenRemoteConsole(bin, "-s", dev, "shell") 135 } 136 137 type remoteCon struct { 138 closeMu sync.Mutex 139 readMu sync.Mutex 140 cmd *exec.Cmd 141 rpipe io.ReadCloser 142 } 143 144 func (t *remoteCon) Read(buf []byte) (int, error) { 145 t.readMu.Lock() 146 n, err := t.rpipe.Read(buf) 147 t.readMu.Unlock() 148 return n, err 149 } 150 151 func (t *remoteCon) Close() error { 152 t.closeMu.Lock() 153 cmd := t.cmd 154 t.cmd = nil 155 t.closeMu.Unlock() 156 if cmd == nil { 157 return nil 158 } 159 160 cmd.Process.Kill() 161 162 t.readMu.Lock() 163 t.rpipe.Close() 164 t.readMu.Unlock() 165 166 cmd.Process.Wait() 167 return nil 168 }