blob: 3a6f5713a0a86c780b7d0ebcfcb4d5becb068849 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package internal
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"path"
"regexp"
"strings"
embeddedagent "chromium.googlesource.com/chromiumos/platform/dev-util.git/contrib/fflash/internal/embedded-agent"
"chromium.googlesource.com/chromiumos/platform/dev-util.git/contrib/fflash/internal/partitions"
"chromium.googlesource.com/chromiumos/platform/dev-util.git/contrib/fflash/internal/progress"
"chromium.googlesource.com/chromiumos/platform/dev-util.git/contrib/fflash/internal/ssh"
)
var boardLineRegexp = regexp.MustCompile(`CHROMEOS_RELEASE_BOARD=(.+?)(?:-signed-.*)?\n`)
// DetectBoard detects the board of c's remote host.
func DetectBoard(c *ssh.Client) (string, error) {
stdout, err := c.RunSimpleOutput("cat /etc/lsb-release")
if err != nil {
return "", err
}
match := boardLineRegexp.FindStringSubmatch(stdout)
if match == nil {
return "", errors.New("cannot find CHROMEOS_RELEASE_BOARD line")
}
return match[1], nil
}
// DetectPartitions detects the active/inactive partition state on c's remote host.
func DetectPartitions(c *ssh.Client) (partitions.State, error) {
rootPart, err := c.RunSimpleOutput("rootdev -s")
if err != nil {
return partitions.State{}, err
}
return partitions.GetStateFromRootPartition(strings.TrimSuffix(rootPart, "\n"))
}
// DetectArch detects the CPU architecture on c's remote host.
func DetectArch(c *ssh.Client) (string, error) {
stdout, err := c.RunSimpleOutput("uname -m")
if err != nil {
return "", err
}
arch := strings.TrimSuffix(stdout, "\n")
switch arch {
case embeddedagent.ArchAarch64, embeddedagent.ArchX8664:
return arch, nil
default:
return "", fmt.Errorf("unknown arch %q", arch)
}
}
// PushCompressedExecutable pushes a compressed executable to c's remote host.
// The path of the pushed executable is returned.
func PushCompressedExecutable(ctx context.Context, c *ssh.Client, b []byte) (string, error) {
tempDir, err := c.RunSimpleOutput("mktemp --directory --tmpdir=/tmp dut-agent.XXXXXXXXXX")
if err != nil {
return "", err
}
tempDir = strings.TrimSuffix(tempDir, "\n")
// Create an in-memory filesystem to put the dut-agent executable
// A in-memory device file is used instead of tmpfs to workaround LSM restrictions
// https://crrev.com/c/3834992
devicePath := path.Join(tempDir, "device")
if _, err := c.RunSimpleOutput("fallocate --length=32M " + devicePath); err != nil {
return "", err
}
if _, err := c.RunSimpleOutput("mkfs.ext4 " + devicePath); err != nil {
return "", err
}
if _, err := c.RunSimpleOutput(fmt.Sprintf("mount %s %s", devicePath, tempDir)); err != nil {
return "", err
}
agentPath := path.Join(tempDir, "dut-agent")
session, err := c.NewSession()
if err != nil {
return "", err
}
defer session.Close()
prog := progress.NewWriter("push", int64(len(b)))
defer prog.Close()
session.Stdin = io.TeeReader(bytes.NewBuffer(b), prog)
_, err = session.SimpleOutput("zstd -d - -o " + agentPath)
if err != nil {
return "", err
}
if _, err := c.RunSimpleOutput("chmod +x " + agentPath); err != nil {
return "", err
}
return agentPath, nil
}