diff --git a/src/runtime/pkg/govmm/qemu/examples_test.go b/src/runtime/pkg/govmm/qemu/examples_test.go index 1a3f2e8ffb..6f6727effb 100644 --- a/src/runtime/pkg/govmm/qemu/examples_test.go +++ b/src/runtime/pkg/govmm/qemu/examples_test.go @@ -32,7 +32,7 @@ func Example() { // flag. // It will set up a unix domain socket called /tmp/qmp-socket that we // can use to manage the instance. - proc, err := qemu.LaunchCustomQemu(context.Background(), "", params, nil, nil, nil) + proc, _, err := qemu.LaunchCustomQemu(context.Background(), "", params, nil, nil, nil) if err != nil { panic(err) } diff --git a/src/runtime/pkg/govmm/qemu/qemu.go b/src/runtime/pkg/govmm/qemu/qemu.go index 10de342ed3..d52362d5f6 100644 --- a/src/runtime/pkg/govmm/qemu/qemu.go +++ b/src/runtime/pkg/govmm/qemu/qemu.go @@ -16,6 +16,7 @@ package qemu import ( "context" "fmt" + "io" "log" "os" "os/exec" @@ -2967,7 +2968,7 @@ func (config *Config) appendFwCfg(logger QMPLog) { // The Config parameter contains a set of qemu parameters and settings. // // See LaunchCustomQemu for more information. -func LaunchQemu(config Config, logger QMPLog) (*exec.Cmd, error) { +func LaunchQemu(config Config, logger QMPLog) (*exec.Cmd, io.ReadCloser, error) { config.appendName() config.appendUUID() config.appendMachine() @@ -2990,7 +2991,7 @@ func LaunchQemu(config Config, logger QMPLog) (*exec.Cmd, error) { config.appendSeccompSandbox() if err := config.appendCPUs(); err != nil { - return nil, err + return nil, nil, err } ctx := config.Ctx @@ -3021,11 +3022,12 @@ func LaunchQemu(config Config, logger QMPLog) (*exec.Cmd, error) { // // This function writes its log output via logger parameter. // -// The function returns cmd, nil where cmd is a Go exec.Cmd object -// representing the QEMU process if launched successfully. Otherwise -// nil, err where err is a Go error object is returned. +// The function returns cmd, reader, nil where cmd is a Go exec.Cmd object +// representing the QEMU process and reader a Go io.ReadCloser object +// connected to QEMU's stderr, if launched successfully. Otherwise +// nil, nil, err where err is a Go error object is returned. func LaunchCustomQemu(ctx context.Context, path string, params []string, fds []*os.File, - attr *syscall.SysProcAttr, logger QMPLog) (*exec.Cmd, error) { + attr *syscall.SysProcAttr, logger QMPLog) (*exec.Cmd, io.ReadCloser, error) { if logger == nil { logger = qmpNullLogger{} } @@ -3043,12 +3045,17 @@ func LaunchCustomQemu(ctx context.Context, path string, params []string, fds []* cmd.SysProcAttr = attr + reader, err := cmd.StderrPipe() + if err != nil { + logger.Errorf("Unable to connect stderr to a pipe") + return nil, nil, err + } logger.Infof("launching %s with: %v", path, params) - err := cmd.Start() + err = cmd.Start() if err != nil { logger.Errorf("Unable to launch %s: %v", path, err) - return nil, err + return nil, nil, err } - return cmd, nil + return cmd, reader, nil } diff --git a/src/runtime/virtcontainers/qemu.go b/src/runtime/virtcontainers/qemu.go index 11b779eb81..8b0b7cb971 100644 --- a/src/runtime/virtcontainers/qemu.go +++ b/src/runtime/virtcontainers/qemu.go @@ -14,11 +14,14 @@ import ( "encoding/hex" "encoding/json" "fmt" + "io" "math" "net" "os" + "os/exec" "os/user" "path/filepath" + "regexp" "strconv" "strings" "sync" @@ -849,6 +852,24 @@ func (q *qemu) setupEarlyQmpConnection() (net.Conn, error) { return conn, nil } +func (q *qemu) LogAndWait(qemuCmd *exec.Cmd, reader io.ReadCloser) { + pid := qemuCmd.Process.Pid + q.Logger().Infof("Start logging QEMU (qemuPid=%d)", pid) + scanner := bufio.NewScanner(reader) + warnRE := regexp.MustCompile("(^[^:]+: )warning: ") + for scanner.Scan() { + text := scanner.Text() + if warnRE.MatchString(text) { + text = warnRE.ReplaceAllString(text, "$1") + q.Logger().WithField("qemuPid", pid).Warning(text) + } else { + q.Logger().WithField("qemuPid", pid).Error(text) + } + } + q.Logger().Infof("Stop logging QEMU (qemuPid=%d)", pid) + qemuCmd.Wait() +} + // StartVM will start the Sandbox's VM. func (q *qemu) StartVM(ctx context.Context, timeout int) error { span, ctx := katatrace.Trace(ctx, q.Logger(), "StartVM", qemuTracingTags, map[string]string{"sandbox_id": q.id}) @@ -926,7 +947,7 @@ func (q *qemu) StartVM(ctx context.Context, timeout int) error { } - qemuCmd, err := govmmQemu.LaunchQemu(q.qemuConfig, newQMPLogger()) + qemuCmd, reader, err := govmmQemu.LaunchQemu(q.qemuConfig, newQMPLogger()) if err != nil { q.Logger().WithError(err).Error("failed to launch qemu") return fmt.Errorf("failed to launch qemu: %s", err) @@ -937,10 +958,9 @@ func (q *qemu) StartVM(ctx context.Context, timeout int) error { // actually started. qemuCmd.Wait() } else { - // Ensure the QEMU process is reaped after termination - go func() { - qemuCmd.Wait() - }() + // Log QEMU errors and ensure the QEMU process is reaped after + // termination. + go q.LogAndWait(qemuCmd, reader) } err = q.waitVM(ctx, qmpConn, timeout)