mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-23 11:29:46 +00:00
vm/vmm: don't start separate process for console
vmctl console fails from time to time with: vmctl: console not found Probably there is some race (most of these things assume that there is a human typing commands with delays). Also, vmctl start can connect to console itself with -c flag. So use that because it both solves the console race and also makes code much more similar to other VM implementations (qemu, gvisor). This also eliminates 3 additional goroutines per VM.
This commit is contained in:
parent
23db2ea6f9
commit
565a5452a1
160
vm/vmm/vmm.go
160
vm/vmm/vmm.go
@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@ -37,19 +38,17 @@ type Pool struct {
|
|||||||
|
|
||||||
type instance struct {
|
type instance struct {
|
||||||
cfg *Config
|
cfg *Config
|
||||||
index int
|
|
||||||
image string
|
image string
|
||||||
debug bool
|
debug bool
|
||||||
os string
|
os string
|
||||||
workdir string
|
|
||||||
sshkey string
|
sshkey string
|
||||||
sshuser string
|
sshuser string
|
||||||
sshhost string
|
sshhost string
|
||||||
sshport int
|
sshport int
|
||||||
merger *vmimpl.OutputMerger
|
merger *vmimpl.OutputMerger
|
||||||
vmName string
|
vmName string
|
||||||
stop chan bool
|
vmm *exec.Cmd
|
||||||
diagnose chan string
|
consolew io.WriteCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
var ipRegex = regexp.MustCompile(`bound to (([0-9]+\.){3}3)`)
|
var ipRegex = regexp.MustCompile(`bound to (([0-9]+\.){3}3)`)
|
||||||
@ -100,21 +99,22 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
name := fmt.Sprintf("%v-%v", pool.env.Name, index)
|
var tee io.Writer
|
||||||
inst := &instance{
|
if pool.env.Debug {
|
||||||
cfg: pool.cfg,
|
tee = os.Stdout
|
||||||
index: index,
|
|
||||||
image: image,
|
|
||||||
debug: pool.env.Debug,
|
|
||||||
os: pool.env.OS,
|
|
||||||
workdir: workdir,
|
|
||||||
sshkey: pool.env.SSHKey,
|
|
||||||
sshuser: pool.env.SSHUser,
|
|
||||||
sshport: 22,
|
|
||||||
vmName: name,
|
|
||||||
stop: make(chan bool),
|
|
||||||
diagnose: make(chan string),
|
|
||||||
}
|
}
|
||||||
|
inst := &instance{
|
||||||
|
cfg: pool.cfg,
|
||||||
|
image: image,
|
||||||
|
debug: pool.env.Debug,
|
||||||
|
os: pool.env.OS,
|
||||||
|
sshkey: pool.env.SSHKey,
|
||||||
|
sshuser: pool.env.SSHUser,
|
||||||
|
sshport: 22,
|
||||||
|
vmName: fmt.Sprintf("%v-%v", pool.env.Name, index),
|
||||||
|
merger: vmimpl.NewOutputMerger(tee),
|
||||||
|
}
|
||||||
|
|
||||||
inst.vmctl("stop", inst.vmName, "-f", "-w") // in case it's still running from the previous run
|
inst.vmctl("stop", inst.vmName, "-f", "-w") // in case it's still running from the previous run
|
||||||
closeInst := inst
|
closeInst := inst
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -132,30 +132,46 @@ func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (inst *instance) Boot() error {
|
func (inst *instance) Boot() error {
|
||||||
mem := fmt.Sprintf("%vM", inst.cfg.Mem)
|
outr, outw, err := osutil.LongPipe()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
inr, inw, err := osutil.LongPipe()
|
||||||
|
if err != nil {
|
||||||
|
outr.Close()
|
||||||
|
outw.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
startArgs := []string{
|
startArgs := []string{
|
||||||
"start", inst.vmName,
|
"start", inst.vmName,
|
||||||
"-b", inst.cfg.Kernel,
|
"-b", inst.cfg.Kernel,
|
||||||
"-d", inst.image,
|
"-d", inst.image,
|
||||||
"-m", mem,
|
"-m", fmt.Sprintf("%vM", inst.cfg.Mem),
|
||||||
"-L", // add a local network interface
|
"-L", // add a local network interface
|
||||||
|
"-c", // connect to the console
|
||||||
}
|
}
|
||||||
if inst.cfg.Template != "" {
|
if inst.cfg.Template != "" {
|
||||||
startArgs = append(startArgs, "-t", inst.cfg.Template)
|
startArgs = append(startArgs, "-t", inst.cfg.Template)
|
||||||
}
|
}
|
||||||
if _, err := inst.vmctl(startArgs...); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var tee io.Writer
|
|
||||||
if inst.debug {
|
if inst.debug {
|
||||||
tee = os.Stdout
|
log.Logf(0, "running command: vmctl %#v", startArgs)
|
||||||
}
|
}
|
||||||
inst.merger = vmimpl.NewOutputMerger(tee)
|
cmd := osutil.Command("vmctl", startArgs...)
|
||||||
|
cmd.Stdin = inr
|
||||||
if err := inst.console(); err != nil {
|
cmd.Stdout = outw
|
||||||
|
cmd.Stderr = outw
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
outr.Close()
|
||||||
|
outw.Close()
|
||||||
|
inr.Close()
|
||||||
|
inw.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
inst.vmm = cmd
|
||||||
|
inst.consolew = inw
|
||||||
|
outw.Close()
|
||||||
|
inr.Close()
|
||||||
|
inst.merger.Add("console", outr)
|
||||||
|
|
||||||
var bootOutput []byte
|
var bootOutput []byte
|
||||||
bootOutputStop := make(chan bool)
|
bootOutputStop := make(chan bool)
|
||||||
@ -202,6 +218,14 @@ func (inst *instance) Boot() error {
|
|||||||
|
|
||||||
func (inst *instance) Close() {
|
func (inst *instance) Close() {
|
||||||
inst.vmctl("stop", inst.vmName, "-f")
|
inst.vmctl("stop", inst.vmName, "-f")
|
||||||
|
if inst.consolew != nil {
|
||||||
|
inst.consolew.Close()
|
||||||
|
}
|
||||||
|
if inst.vmm != nil {
|
||||||
|
inst.vmm.Process.Kill()
|
||||||
|
inst.vmm.Wait()
|
||||||
|
}
|
||||||
|
inst.merger.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inst *instance) Forward(port int) (string, error) {
|
func (inst *instance) Forward(port int) (string, error) {
|
||||||
@ -264,95 +288,33 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin
|
|||||||
signal(vmimpl.ErrTimeout)
|
signal(vmimpl.ErrTimeout)
|
||||||
case err := <-inst.merger.Err:
|
case err := <-inst.merger.Err:
|
||||||
cmd.Process.Kill()
|
cmd.Process.Kill()
|
||||||
inst.stop <- true
|
|
||||||
inst.merger.Wait()
|
|
||||||
if cmdErr := cmd.Wait(); cmdErr == nil {
|
if cmdErr := cmd.Wait(); cmdErr == nil {
|
||||||
// If the command exited successfully, we got EOF error from merger.
|
// If the command exited successfully, we got EOF error from merger.
|
||||||
// But in this case no error has happened and the EOF is expected.
|
// But in this case no error has happened and the EOF is expected.
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
signal(err)
|
signal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cmd.Process.Kill()
|
cmd.Process.Kill()
|
||||||
inst.stop <- true
|
|
||||||
inst.merger.Wait()
|
|
||||||
cmd.Wait()
|
cmd.Wait()
|
||||||
}()
|
}()
|
||||||
return inst.merger.Output, errc, nil
|
return inst.merger.Output, errc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inst *instance) Diagnose() bool {
|
func (inst *instance) Diagnose() bool {
|
||||||
commands := []string{"", "trace", "show registers"}
|
// TODO(dvyukov): this does not work because console asks for login:
|
||||||
for _, c := range commands {
|
// OpenBSD/amd64 (syzkaller.my.domain) (tty00)
|
||||||
select {
|
// login: trace
|
||||||
case inst.diagnose <- c:
|
// Password:
|
||||||
case <-time.After(5 * time.Second):
|
// Login incorrect
|
||||||
}
|
for _, c := range []string{"\n", "trace\n", "show registers\n"} {
|
||||||
|
inst.consolew.Write([]byte(c))
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inst *instance) console() error {
|
|
||||||
outr, outw, err := osutil.LongPipe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
inr, inw, err := osutil.LongPipe()
|
|
||||||
if err != nil {
|
|
||||||
outr.Close()
|
|
||||||
outw.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := osutil.Command("vmctl", "console", inst.vmName)
|
|
||||||
cmd.Stdin = inr
|
|
||||||
cmd.Stdout = outw
|
|
||||||
cmd.Stderr = outw
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
outr.Close()
|
|
||||||
outw.Close()
|
|
||||||
inr.Close()
|
|
||||||
inw.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
outw.Close()
|
|
||||||
inr.Close()
|
|
||||||
inst.merger.Add("console", outr)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
stopDiagnose := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case s := <-inst.diagnose:
|
|
||||||
inw.Write([]byte(s + "\n"))
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
case <-stopDiagnose:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
stopProcess := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-inst.stop:
|
|
||||||
cmd.Process.Kill()
|
|
||||||
case <-stopProcess:
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
_, err = cmd.Process.Wait()
|
|
||||||
inw.Close()
|
|
||||||
stopDiagnose <- true
|
|
||||||
stopProcess <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the given vmctl(8) command and wait for it to finish.
|
// Run the given vmctl(8) command and wait for it to finish.
|
||||||
func (inst *instance) vmctl(args ...string) (string, error) {
|
func (inst *instance) vmctl(args ...string) (string, error) {
|
||||||
if inst.debug {
|
if inst.debug {
|
||||||
|
Loading…
Reference in New Issue
Block a user