vm/{adb,isolated}: dudup common code

Found with gometalinter/dupl.

Update #538
diff --git a/vm/adb/adb.go b/vm/adb/adb.go
index 21fd5f7..e6908a9 100644
--- a/vm/adb/adb.go
+++ b/vm/adb/adb.go
@@ -422,41 +422,5 @@
 	merger.Add("console", tty)
 	merger.Add("adb", adbRpipe)
 
-	errc := make(chan error, 1)
-	signal := func(err error) {
-		select {
-		case errc <- err:
-		default:
-		}
-	}
-
-	go func() {
-		select {
-		case <-time.After(timeout):
-			signal(vmimpl.ErrTimeout)
-		case <-stop:
-			signal(vmimpl.ErrTimeout)
-		case <-inst.closed:
-			if inst.debug {
-				log.Logf(0, "instance closed")
-			}
-			signal(fmt.Errorf("instance closed"))
-		case err := <-merger.Err:
-			adb.Process.Kill()
-			tty.Close()
-			merger.Wait()
-			if cmdErr := adb.Wait(); cmdErr == nil {
-				// If the command exited successfully, we got EOF error from merger.
-				// But in this case no error has happened and the EOF is expected.
-				err = nil
-			}
-			signal(err)
-			return
-		}
-		adb.Process.Kill()
-		tty.Close()
-		merger.Wait()
-		adb.Wait()
-	}()
-	return merger.Output, errc, nil
+	return vmimpl.Multiplex(adb, merger, tty, timeout, stop, inst.closed, inst.debug)
 }
diff --git a/vm/isolated/isolated.go b/vm/isolated/isolated.go
index 4a6bf7f..86adb80 100644
--- a/vm/isolated/isolated.go
+++ b/vm/isolated/isolated.go
@@ -305,43 +305,7 @@
 	merger.Add("dmesg", dmesg)
 	merger.Add("ssh", rpipe)
 
-	errc := make(chan error, 1)
-	signal := func(err error) {
-		select {
-		case errc <- err:
-		default:
-		}
-	}
-
-	go func() {
-		select {
-		case <-time.After(timeout):
-			signal(vmimpl.ErrTimeout)
-		case <-stop:
-			signal(vmimpl.ErrTimeout)
-		case <-inst.closed:
-			if inst.debug {
-				log.Logf(0, "instance closed")
-			}
-			signal(fmt.Errorf("instance closed"))
-		case err := <-merger.Err:
-			cmd.Process.Kill()
-			dmesg.Close()
-			merger.Wait()
-			if cmdErr := cmd.Wait(); cmdErr == nil {
-				// If the command exited successfully, we got EOF error from merger.
-				// But in this case no error has happened and the EOF is expected.
-				err = nil
-			}
-			signal(err)
-			return
-		}
-		cmd.Process.Kill()
-		dmesg.Close()
-		merger.Wait()
-		cmd.Wait()
-	}()
-	return merger.Output, errc, nil
+	return vmimpl.Multiplex(cmd, merger, dmesg, timeout, stop, inst.closed, inst.debug)
 }
 
 func (inst *instance) sshArgs(portArg string) []string {
diff --git a/vm/vmimpl/vmimpl.go b/vm/vmimpl/vmimpl.go
index 4565d17..a4dba2f 100644
--- a/vm/vmimpl/vmimpl.go
+++ b/vm/vmimpl/vmimpl.go
@@ -10,7 +10,11 @@
 import (
 	"errors"
 	"fmt"
+	"io"
+	"os/exec"
 	"time"
+
+	"github.com/google/syzkaller/pkg/log"
 )
 
 // Pool represents a set of test machines (VMs, physical devices, etc) of particular type.
@@ -93,3 +97,43 @@
 )
 
 type ctorFunc func(env *Env) (Pool, error)
+
+func Multiplex(cmd *exec.Cmd, merger *OutputMerger, console io.Closer, timeout time.Duration,
+	stop, closed <-chan bool, debug bool) (<-chan []byte, <-chan error, error) {
+	errc := make(chan error, 1)
+	signal := func(err error) {
+		select {
+		case errc <- err:
+		default:
+		}
+	}
+	go func() {
+		select {
+		case <-time.After(timeout):
+			signal(ErrTimeout)
+		case <-stop:
+			signal(ErrTimeout)
+		case <-closed:
+			if debug {
+				log.Logf(0, "instance closed")
+			}
+			signal(fmt.Errorf("instance closed"))
+		case err := <-merger.Err:
+			cmd.Process.Kill()
+			console.Close()
+			merger.Wait()
+			if cmdErr := cmd.Wait(); cmdErr == nil {
+				// If the command exited successfully, we got EOF error from merger.
+				// But in this case no error has happened and the EOF is expected.
+				err = nil
+			}
+			signal(err)
+			return
+		}
+		cmd.Process.Kill()
+		console.Close()
+		merger.Wait()
+		cmd.Wait()
+	}()
+	return merger.Output, errc, nil
+}