blob: 713e28e45bd7e768c58fbaa94483cf8151ea0029 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package audioutils provides the util functions needed for the vm audio tests
package audioutils
import (
"context"
"fmt"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"strconv"
"strings"
"syscall"
"chromiumos/tast/common/testexec"
"chromiumos/tast/errors"
"chromiumos/tast/testing"
)
const (
cgroupPath string = "/sys/fs/cgroup/cpu/vms/termina/tasks"
)
// Config includes all the params needed to setup crosvm for vm audio tests.
type Config struct {
CrosvmArgs []string
VhostUserArgs []string
}
// RunCrosvm runs crosvm and the crosvm vhost user device if required.
func RunCrosvm(ctx context.Context, kernelPath, kernelLogPath string, kernelArgs []string, config Config) error {
crosvmCmd, devCmd, cleanupFunc, err := crosvmCmd(ctx, kernelPath, kernelLogPath, kernelArgs, config)
if err != nil {
return errors.Wrap(err, "failed to get crosvm cmd")
}
defer cleanupFunc()
if devCmd != nil {
testing.ContextLog(ctx, "Starting crosvm device")
if err = devCmd.Start(); err != nil {
return errors.Wrap(err, "failed to start crosvm device")
}
}
testing.ContextLog(ctx, "Launching crosvm")
if err = crosvmCmd.Run(testexec.DumpLogOnError); err != nil {
return errors.Wrap(err, "failed to run crosvm")
}
if devCmd != nil {
if err = devCmd.Wait(testexec.DumpLogOnError); err != nil {
return errors.Wrap(err, "failed to complete vhost-user-snd-device")
}
}
return nil
}
// crosvmCmd setups the crosvm and device command for audio device testing.
func crosvmCmd(ctx context.Context, kernelPath, kernelLogPath string, kernelArgs []string, config Config) (crosvmCmd, devCmd *testexec.Cmd, cleanupFunc func() error, retErr error) {
cleanupFunc = func() error { return nil }
defer func() {
if retErr != nil {
if err := cleanupFunc(); err != nil {
testing.ContextLog(ctx, "Failed to cleanup: ", err)
}
cleanupFunc = nil
}
}()
if len(config.VhostUserArgs) > 0 {
tempDir, err := ioutil.TempDir("/usr/local/tmp", "CrosvmCmd.")
if err != nil {
return nil, nil, cleanupFunc, errors.Wrap(err, "failed to create temporary directory")
}
cleanupFunc = func() error {
return os.RemoveAll(tempDir)
}
sock := filepath.Join(tempDir, "vhost-user-snd.sock")
deviceArgs := append([]string{"device"}, config.VhostUserArgs...)
deviceArgs = append(deviceArgs, "--socket", sock)
devCmd = testexec.CommandContext(ctx, "crosvm", deviceArgs...)
config.CrosvmArgs = append(config.CrosvmArgs, "--vhost-user-snd", sock)
}
kernelParams := []string{
"root=/dev/root",
"rootfstype=virtiofs",
"rw",
}
kernelParams = append(kernelParams, kernelArgs...)
crosvmArgs := []string{"crosvm", "run"}
crosvmArgs = append(crosvmArgs, config.CrosvmArgs...)
crosvmArgs = append(crosvmArgs,
"-p", "\""+strings.Join(kernelParams, " ")+"\"",
"--serial", fmt.Sprintf("type=file,num=1,console=true,path=%s", kernelLogPath),
"--shared-dir", "/:/dev/root:type=fs:cache=always",
kernelPath)
// Add the shell process id to the control group
cmdStr := []string{"echo $$ >", cgroupPath, "&&"}
// Set the rtprio limit of the shell process to unlimited.
cmdStr = append(cmdStr, "prlimit", "--pid", "$$", "--rtprio=unlimited", "&&")
cmdStr = append(cmdStr, crosvmArgs...)
crosvmCmd = testexec.CommandContext(ctx, "sh", []string{"-c", strings.Join(cmdStr, " ")}...)
if devCmd == nil {
// Same effect as calling `newgrp cras` before `crosvm` in shell
// This is needed to access /run/cras/.cras_socket (legacy socket)
//
// vhost-user device does not need this as it doesn't involve minijail.
crasGrp, err := user.LookupGroup("cras")
if err != nil {
return nil, nil, cleanupFunc, errors.Wrap(err, "failed to find group id for cras")
}
crasGrpID, err := strconv.ParseUint(crasGrp.Gid, 10, 32)
if err != nil {
return nil, nil, cleanupFunc, errors.Wrap(err, "failed to convert cras grp id to integer")
}
crosvmCmd.Cred(syscall.Credential{
Uid: 0,
Gid: 0,
Groups: []uint32{uint32(crasGrpID)},
NoSetGroups: false,
})
}
return crosvmCmd, devCmd, cleanupFunc, nil
}