github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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 return max(n, 0), err 65 } 66 67 func (t *tty) Close() error { 68 t.mu.Lock() 69 defer t.mu.Unlock() 70 if t.fd != -1 { 71 syscall.Close(t.fd) 72 t.fd = -1 73 } 74 return nil 75 } 76 77 // OpenRemoteKernelLog accesses to the host where Android VM runs on, not Android VM itself. 78 // The host stores all kernel outputs of Android VM so in case of crashes nothing will be lost. 79 func OpenRemoteKernelLog(ip, console string) (rc io.ReadCloser, err error) { 80 conAddr := "vsoc-01@" + ip 81 args := []string{ 82 conAddr, 83 "tail", 84 "-f", 85 console, 86 } 87 return OpenConsoleByCmd("ssh", args) 88 } 89 90 // Open dmesg remotely. 91 func OpenRemoteConsole(bin string, args ...string) (rc io.ReadCloser, err error) { 92 args = append(args, "dmesg -w") 93 return OpenConsoleByCmd(bin, args) 94 } 95 96 // OpenAdbConsole provides fallback console output using 'adb shell dmesg -w'. 97 func OpenAdbConsole(bin, dev string) (rc io.ReadCloser, err error) { 98 return OpenRemoteConsole(bin, "-s", dev, "shell") 99 } 100 101 // Open console log by cmd. 102 func OpenConsoleByCmd(bin string, args []string) (rc io.ReadCloser, err error) { 103 rpipe, wpipe, err := osutil.LongPipe() 104 if err != nil { 105 return nil, err 106 } 107 cmd := osutil.Command(bin, args...) 108 cmd.Stdout = wpipe 109 cmd.Stderr = wpipe 110 if _, err := cmd.StdinPipe(); err != nil { 111 rpipe.Close() 112 wpipe.Close() 113 return nil, err 114 } 115 if err := cmd.Start(); err != nil { 116 rpipe.Close() 117 wpipe.Close() 118 return nil, fmt.Errorf("failed to open console: %w", err) 119 } 120 wpipe.Close() 121 con := &remoteCon{ 122 cmd: cmd, 123 rpipe: rpipe, 124 } 125 return con, err 126 } 127 128 type remoteCon struct { 129 closeMu sync.Mutex 130 readMu sync.Mutex 131 cmd *exec.Cmd 132 rpipe io.ReadCloser 133 } 134 135 func (t *remoteCon) Read(buf []byte) (int, error) { 136 t.readMu.Lock() 137 n, err := t.rpipe.Read(buf) 138 t.readMu.Unlock() 139 return n, err 140 } 141 142 func (t *remoteCon) Close() error { 143 t.closeMu.Lock() 144 cmd := t.cmd 145 t.cmd = nil 146 t.closeMu.Unlock() 147 if cmd == nil { 148 return nil 149 } 150 151 cmd.Process.Kill() 152 153 t.readMu.Lock() 154 t.rpipe.Close() 155 t.readMu.Unlock() 156 157 cmd.Process.Wait() 158 return nil 159 }