mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-11-02 05:35:41 +08:00
Add repository-local tutorial agent skills (#218)
* Add repository-local tutorial agent skills * Enforce tutorial writer safety boundaries * Reduce trace verifier complexity * Add cross-model tutorial writing review * Refresh apt metadata in libbpf CI * Split tutorial workflow and writing style skills * Check complete tutorial source blocks * Harden the tutorial writing workflow * Refactor source block validation * Close tutorial workflow isolation gaps * Test the isolated writer trace verifier * Keep complete-source synchronization byte exact * Separate tutorial and Skill review gates * Simplify the tutorial writing workflow * Keep tutorial source and explanations together * Keep public tutorial references self-contained * Restore tutorial checker executable bits * Refine tutorial writing workflow * Prefer positive tutorial prose * Add diagram guidance to tutorial skills * Standardize tutorial source links --------- Co-authored-by: LinuxDev9002 <i@littlefisher.me>
This commit is contained in:
118
.agents/skills/test-bpf-tutorial-kvm/SKILL.md
Normal file
118
.agents/skills/test-bpf-tutorial-kvm/SKILL.md
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
name: test-bpf-tutorial-kvm
|
||||
description: Build and smoke-test bpf-developer-tutorial lessons without loading BPF programs into the host kernel. Use when creating, changing, reviewing, or debugging an eBPF tutorial and the runtime test should reuse the already-built x86 kernel under bpf-benchmark through virtme-ng/KVM. Also use for checking whether a tutorial needs a newer kernel feature. Do not use this workflow for performance benchmarking or for rebuilding the benchmark kernel.
|
||||
---
|
||||
|
||||
# Test BPF Tutorials in KVM
|
||||
|
||||
Compile tutorial code on the host, but run every command that loads, attaches, or exercises BPF inside a short-lived KVM guest using the kernel artifact already built by `bpf-benchmark`.
|
||||
|
||||
## Safety boundary
|
||||
|
||||
- Never load or attach a tutorial BPF program on the host. This includes `bpftool prog load`, loaders, `make run`, XDP/TC attachment, sched_ext registration, and LSM/cgroup attachment.
|
||||
- Do not rebuild or modify `bpf-benchmark`, its kernel tree, its Git state, caches, results, or conversation/agent-history data.
|
||||
- Do not silently fall back to the host kernel or to TCG. Stop if `/dev/kvm`, `vng`, or the expected benchmark kernel is unavailable.
|
||||
- Treat the tutorial directory as read-only in the guest by default. Use `--rwdir` only for a path that must return guest-generated files to the host.
|
||||
- Use bounded commands. The wrapper applies a host-side timeout; add a shorter guest-side `timeout` to long-running loaders when useful.
|
||||
- Report a KVM run as a functional smoke test, not as benchmark-quality evidence.
|
||||
- Keep this infrastructure private in public prose. A tutorial or PR description may state the architecture, kernel version and commit, configuration, commands, and captured output, but must not name `bpf-benchmark`, local paths, VM instances, mount routes, caches, or agent-state locations.
|
||||
|
||||
## Default locations
|
||||
|
||||
Resolve the tutorial checkout and skill from the current Git worktree:
|
||||
|
||||
```bash
|
||||
TUTORIAL_ROOT="$(git rev-parse --show-toplevel)"
|
||||
RUNNER="$TUTORIAL_ROOT/.agents/skills/test-bpf-tutorial-kvm/scripts/run-in-kvm.sh"
|
||||
```
|
||||
|
||||
The helper reads `BPF_BENCHMARK_ROOT`, defaulting to a `bpf-benchmark` checkout next to the tutorial repository. Its `--cwd` defaults to the caller's current directory.
|
||||
|
||||
The reused kernel is:
|
||||
|
||||
```text
|
||||
$BPF_BENCHMARK_ROOT/vendor/build/x86/linux/arch/x86/boot/bzImage
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Validate the repositories and instructions
|
||||
|
||||
Read every applicable `AGENTS.md` or equivalent repository instruction before acting.
|
||||
|
||||
Require a real tutorial checkout before implementation or testing. A valid checkout has lesson sources and a working Git worktree, not only a `.git` fragment. If it is incomplete, stop and explain that the checkout must be restored; do not clone over it or delete the remnant without explicit authorization.
|
||||
|
||||
The top-level `bpf-benchmark` Git repository does not need to be healthy for artifact reuse. The wrapper validates the kernel image, build config, kernel source worktree, KVM device, and toolchain independently.
|
||||
|
||||
Run the preflight:
|
||||
|
||||
```bash
|
||||
"$RUNNER" --check
|
||||
```
|
||||
|
||||
### 2. Inspect before building
|
||||
|
||||
Read the lesson README, Makefile, source, and existing test commands. Identify separately:
|
||||
|
||||
1. a compile-only host command;
|
||||
2. the userspace loader or runtime command;
|
||||
3. the event or workload that proves the program works;
|
||||
4. any required network, cgroup, tracefs, debugfs, or kernel-config feature.
|
||||
|
||||
Use `make -n` or inspect recipes before choosing a target. A target that invokes a loader is a guest command, even if it also compiles.
|
||||
|
||||
### 3. Build on the host
|
||||
|
||||
Run only compile and static-generation steps on the host. Prefer the lesson's documented build target. Preserve generated `.bpf.o`, skeletons, and userspace binaries in the lesson directory so the guest sees the exact host-built artifacts.
|
||||
|
||||
Do not use `sudo` for the build. Do not run a post-build loader on the host.
|
||||
|
||||
### 4. Run inside the benchmark-kernel guest
|
||||
|
||||
Pass the runtime command after `--`. Arguments are preserved without host re-evaluation.
|
||||
|
||||
```bash
|
||||
TUTORIAL_ROOT="$(git rev-parse --show-toplevel)"
|
||||
RUNNER="$TUTORIAL_ROOT/.agents/skills/test-bpf-tutorial-kvm/scripts/run-in-kvm.sh"
|
||||
LESSON="$TUTORIAL_ROOT/src/<lesson>"
|
||||
|
||||
"$RUNNER" --cwd "$LESSON" -- ./loader --help
|
||||
```
|
||||
|
||||
The wrapper boots the built benchmark `bzImage` with KVM, verifies the guest release, runs as guest root, mounts bpffs/debugfs/tracefs/cgroup2, prints provenance, and powers off when the command exits. It defaults to 2 vCPUs, 4G RAM, no network, and 120 seconds.
|
||||
|
||||
Use `--network user` only when the test needs an external/userspace-backed interface. Use `--network loop` for a guest-local loop network. Prefer `BPF_PROG_TEST_RUN` or deterministic guest-local traffic when either is sufficient.
|
||||
|
||||
Read [the tutorial test patterns](https://github.com/eunomia-bpf/bpf-developer-tutorial/blob/main/.agents/skills/test-bpf-tutorial-kvm/references/tutorial-patterns.md) for tracer, network, writable-output, and failure-diagnosis patterns.
|
||||
|
||||
### 5. Verify the behavior, not only the exit code
|
||||
|
||||
Capture enough evidence to connect an input event to the expected BPF output: attach confirmation, a deterministic trigger in the same guest, expected map/event output, and clean detach or process exit. Negative tests should verify the expected verifier or attach failure.
|
||||
|
||||
For a new lesson, test at least:
|
||||
|
||||
- host compile from a clean lesson build state when safe;
|
||||
- KVM preflight;
|
||||
- one successful guest runtime path;
|
||||
- one relevant error or cleanup path;
|
||||
- documentation commands exactly as written.
|
||||
|
||||
Do not clean unrelated files or repository-wide caches to create a clean build state.
|
||||
|
||||
### 6. Report provenance and limitations
|
||||
|
||||
Include the guest `uname -r`, benchmark kernel source commit, kernel image SHA-256, lesson commit or dirty state, host build command, guest command, and the observed result. The wrapper prints kernel provenance automatically.
|
||||
|
||||
If the tutorial requires a feature absent from the built config or kernel, report the missing symbol/helper/attach type and stop. Propose a benchmark-kernel rebuild or upgrade separately; do not perform it as a fallback.
|
||||
|
||||
## Wrapper interface
|
||||
|
||||
```text
|
||||
run-in-kvm.sh --check
|
||||
run-in-kvm.sh [--cwd PATH] [--cpus N] [--memory SIZE]
|
||||
[--timeout SECONDS] [--network none|user|loop]
|
||||
[--rwdir PATH]... [--append KERNEL_ARGS]...
|
||||
[--dry-run] [--verbose] -- COMMAND [ARG...]
|
||||
```
|
||||
|
||||
Use `--dry-run` to inspect the generated `vng`/QEMU launch without booting. `--rwdir` is an explicit trust decision because guest root can change that host path.
|
||||
4
.agents/skills/test-bpf-tutorial-kvm/agents/openai.yaml
Normal file
4
.agents/skills/test-bpf-tutorial-kvm/agents/openai.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Test BPF Tutorials in KVM"
|
||||
short_description: "Build locally and test with the benchmark KVM kernel"
|
||||
default_prompt: "Use $test-bpf-tutorial-kvm to build this BPF tutorial locally and run its smoke test with the bpf-benchmark KVM kernel."
|
||||
@@ -0,0 +1,89 @@
|
||||
# BPF tutorial KVM test patterns
|
||||
|
||||
Resolve the wrapper from the tutorial worktree:
|
||||
|
||||
```bash
|
||||
TUTORIAL_ROOT="$(git rev-parse --show-toplevel)"
|
||||
RUNNER="$TUTORIAL_ROOT/.agents/skills/test-bpf-tutorial-kvm/scripts/run-in-kvm.sh"
|
||||
```
|
||||
|
||||
All paths below are host paths exposed at the same location in the virtme-ng guest.
|
||||
|
||||
## One-shot loader or self-test
|
||||
|
||||
```bash
|
||||
"$RUNNER" --cwd "$LESSON" -- ./loader --self-test
|
||||
```
|
||||
|
||||
Prefer a self-test that loads the BPF object, exercises a deterministic input, checks the map or return value, and detaches before exiting.
|
||||
|
||||
## Long-running tracer plus a trigger
|
||||
|
||||
The loader, trigger, signal, and output assertion must all occur inside the same guest:
|
||||
|
||||
```bash
|
||||
"$RUNNER" --cwd "$LESSON" --timeout 90 -- bash -lc '
|
||||
set -euo pipefail
|
||||
out=$(mktemp)
|
||||
./tracer >"$out" 2>&1 &
|
||||
pid=$!
|
||||
trap '\''kill -INT "$pid" 2>/dev/null || true; wait "$pid" 2>/dev/null || true'\'' EXIT
|
||||
sleep 1
|
||||
true # replace with the deterministic traced event
|
||||
sleep 1
|
||||
kill -INT "$pid"
|
||||
wait "$pid" || test $? -eq 130
|
||||
trap - EXIT
|
||||
grep -F "expected event" "$out"
|
||||
'
|
||||
```
|
||||
|
||||
Keep the host timeout longer than the guest-side lifecycle. Avoid an unbounded `timeout ./tracer` assertion because exit 124 proves only that it was killed.
|
||||
|
||||
## XDP or TC
|
||||
|
||||
For a userspace-backed interface and outbound guest traffic:
|
||||
|
||||
```bash
|
||||
"$RUNNER" --cwd "$LESSON" --network user -- bash -lc '
|
||||
set -euo pipefail
|
||||
ip -brief link
|
||||
./loader --interface eth0 &
|
||||
pid=$!
|
||||
trap '\''kill -INT "$pid" 2>/dev/null || true; wait "$pid" 2>/dev/null || true'\'' EXIT
|
||||
ping -c 1 -W 2 10.0.2.2
|
||||
# Assert counters or emitted events here.
|
||||
'
|
||||
```
|
||||
|
||||
Use `--network loop` for guest-local network testing. Prefer `BPF_PROG_TEST_RUN` when the lesson is about program behavior rather than attachment mechanics.
|
||||
|
||||
## sched_ext, LSM, and cgroup programs
|
||||
|
||||
- Verify the exact config symbol in the printed kernel config before booting, for example `CONFIG_SCHED_CLASS_EXT=y` or `CONFIG_BPF_LSM=y`.
|
||||
- Keep sched_ext registration bounded and assert that the scheduler exits or unregisters cleanly.
|
||||
- Create test cgroups only inside the guest's cgroup2 mount.
|
||||
- Test LSM enforcement on guest-local temporary files or processes, never on host resources mounted read-write.
|
||||
|
||||
## Returning generated output
|
||||
|
||||
The tutorial directory is read-only by default. Create a dedicated output directory and expose only it when a test must retain logs or generated files:
|
||||
|
||||
```bash
|
||||
mkdir -p "$LESSON/test-output"
|
||||
"$RUNNER" --cwd "$LESSON" --rwdir "$LESSON/test-output" -- \
|
||||
./loader --output "$LESSON/test-output/smoke.json"
|
||||
```
|
||||
|
||||
Guest root can modify any `--rwdir` path. Never expose a repository root, `.git`, credentials, task histories, or agent traces just for convenience.
|
||||
|
||||
## Interpreting failures
|
||||
|
||||
- **Preflight fails:** repair KVM/tool/artifact availability; do not fall back to the host kernel.
|
||||
- **Guest kernel mismatch:** the image and build metadata are inconsistent; rebuild or repair the benchmark artifact separately.
|
||||
- **`bpftool` asks for `linux-tools-<guest-version>`:** the Ubuntu `/usr/sbin/bpftool` wrapper was selected. On this machine, use the real binary at `/usr/local/sbin/bpftool` after confirming it with `--version`.
|
||||
- **Unknown helper, attach type, or kfunc:** compare lesson requirements with the built kernel release/config/BTF. Report a kernel gap rather than changing the host test boundary.
|
||||
- **Permission error inside guest:** confirm the command is running as guest root and that bpffs/debugfs/tracefs/cgroup2 mounted. Do not add host `sudo`.
|
||||
- **Read-only filesystem:** write under `/tmp`, or explicitly expose a narrow output directory with `--rwdir`.
|
||||
- **Timeout 124:** inspect loader cleanup and the trigger. A timeout is not a passing tracer test.
|
||||
- **Verifier rejection:** preserve the complete verifier log and distinguish an intentional negative test from a compatibility regression.
|
||||
320
.agents/skills/test-bpf-tutorial-kvm/scripts/run-in-kvm.sh
Executable file
320
.agents/skills/test-bpf-tutorial-kvm/scripts/run-in-kvm.sh
Executable file
@@ -0,0 +1,320 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
run-in-kvm.sh --check
|
||||
run-in-kvm.sh [options] -- COMMAND [ARG...]
|
||||
|
||||
Build tutorial artifacts on the host, then use this wrapper only for the
|
||||
runtime command that must execute in the bpf-benchmark KVM kernel.
|
||||
|
||||
Options:
|
||||
--check Validate KVM, vng, and the benchmark kernel, then exit.
|
||||
--cwd PATH Guest working directory (default: current directory).
|
||||
--cpus N Guest vCPU count (default: 2).
|
||||
--memory SIZE Guest RAM accepted by vng/QEMU (default: 4G).
|
||||
--timeout SECONDS Host-side wall-clock limit (default: 120).
|
||||
--network MODE none, user, or loop (default: none).
|
||||
--rwdir PATH Explicit host path writable by guest root; repeatable.
|
||||
--append ARGS Additional kernel command-line string; repeatable.
|
||||
--dry-run Print vng's QEMU command without booting.
|
||||
--verbose Show the guest boot console and vng details.
|
||||
-h, --help Show this help.
|
||||
|
||||
Environment:
|
||||
BPF_BENCHMARK_ROOT Default: bpf-benchmark next to this repository
|
||||
EOF
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_command() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
|
||||
}
|
||||
|
||||
require_positive_integer() {
|
||||
local name=$1 value=$2
|
||||
[[ $value =~ ^[1-9][0-9]*$ ]] || die "$name must be a positive integer: $value"
|
||||
}
|
||||
|
||||
quote_for_posix_shell() {
|
||||
local value=$1
|
||||
value=${value//\'/\'\\\'\'}
|
||||
REPLY="'$value'"
|
||||
}
|
||||
|
||||
benchmark_root=${BPF_BENCHMARK_ROOT:-}
|
||||
cwd=$PWD
|
||||
cpus=2
|
||||
memory=4G
|
||||
wall_timeout=120
|
||||
network=none
|
||||
check_only=false
|
||||
dry_run=false
|
||||
verbose=false
|
||||
declare -a rwdirs=()
|
||||
declare -a extra_append=()
|
||||
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
--check)
|
||||
check_only=true
|
||||
shift
|
||||
;;
|
||||
--cwd)
|
||||
(($# >= 2)) || die "--cwd requires a path"
|
||||
cwd=$2
|
||||
shift 2
|
||||
;;
|
||||
--cpus)
|
||||
(($# >= 2)) || die "--cpus requires a value"
|
||||
cpus=$2
|
||||
shift 2
|
||||
;;
|
||||
--memory)
|
||||
(($# >= 2)) || die "--memory requires a value"
|
||||
memory=$2
|
||||
shift 2
|
||||
;;
|
||||
--timeout)
|
||||
(($# >= 2)) || die "--timeout requires seconds"
|
||||
wall_timeout=$2
|
||||
shift 2
|
||||
;;
|
||||
--network)
|
||||
(($# >= 2)) || die "--network requires a mode"
|
||||
network=$2
|
||||
shift 2
|
||||
;;
|
||||
--rwdir)
|
||||
(($# >= 2)) || die "--rwdir requires a path"
|
||||
rwdirs+=("$2")
|
||||
shift 2
|
||||
;;
|
||||
--append)
|
||||
(($# >= 2)) || die "--append requires a kernel command-line string"
|
||||
extra_append+=("$2")
|
||||
shift 2
|
||||
;;
|
||||
--dry-run)
|
||||
dry_run=true
|
||||
shift
|
||||
;;
|
||||
--verbose)
|
||||
verbose=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
die "unknown option before --: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
require_positive_integer "--cpus" "$cpus"
|
||||
require_positive_integer "--timeout" "$wall_timeout"
|
||||
[[ $memory =~ ^[1-9][0-9]*([KkMmGgTt])?$ ]] || die "invalid --memory value: $memory"
|
||||
case $network in
|
||||
none|user|loop) ;;
|
||||
*) die "--network must be none, user, or loop: $network" ;;
|
||||
esac
|
||||
|
||||
for command_name in file flock git mktemp realpath sha256sum timeout vng; do
|
||||
require_command "$command_name"
|
||||
done
|
||||
|
||||
if [[ -z $benchmark_root ]]; then
|
||||
skill_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
tutorial_root=$(git -C "$skill_dir" rev-parse --show-toplevel)
|
||||
benchmark_root=$(dirname -- "$tutorial_root")/bpf-benchmark
|
||||
fi
|
||||
|
||||
[[ $(uname -m) == x86_64 ]] || die "the reused artifact is x86_64, host is $(uname -m)"
|
||||
[[ -c /dev/kvm && -r /dev/kvm && -w /dev/kvm ]] || \
|
||||
die "/dev/kvm is not available with read/write permission; refusing a TCG fallback"
|
||||
|
||||
benchmark_root=$(realpath -e -- "$benchmark_root")
|
||||
kernel_source=$benchmark_root/vendor/linux-framework
|
||||
kernel_build=$benchmark_root/vendor/build/x86/linux
|
||||
kernel_image=$kernel_build/arch/x86/boot/bzImage
|
||||
kernel_config=$kernel_build/.config
|
||||
kernel_release_file=$kernel_build/include/config/kernel.release
|
||||
|
||||
[[ -d $kernel_source ]] || die "kernel source directory is missing: $kernel_source"
|
||||
[[ -s $kernel_image ]] || die "built benchmark kernel is missing: $kernel_image"
|
||||
[[ -r $kernel_config ]] || die "kernel config is missing: $kernel_config"
|
||||
[[ -r $kernel_release_file ]] || die "kernel release file is missing: $kernel_release_file"
|
||||
file -- "$kernel_image" | grep -q 'Linux kernel x86 boot executable bzImage' || \
|
||||
die "artifact is not an x86 bzImage: $kernel_image"
|
||||
git -C "$kernel_source" rev-parse --verify HEAD >/dev/null 2>&1 || \
|
||||
die "kernel source Git worktree is invalid: $kernel_source"
|
||||
|
||||
required_config=(
|
||||
CONFIG_BPF=y
|
||||
CONFIG_BPF_SYSCALL=y
|
||||
CONFIG_BPF_JIT=y
|
||||
CONFIG_CGROUP_BPF=y
|
||||
CONFIG_KPROBES=y
|
||||
CONFIG_UPROBES=y
|
||||
CONFIG_FTRACE=y
|
||||
CONFIG_DEBUG_INFO_BTF=y
|
||||
)
|
||||
for symbol in "${required_config[@]}"; do
|
||||
grep -qxF "$symbol" "$kernel_config" || die "required kernel config is absent: $symbol"
|
||||
done
|
||||
|
||||
kernel_release=$(<"$kernel_release_file")
|
||||
[[ -n $kernel_release ]] || die "kernel release file is empty: $kernel_release_file"
|
||||
kernel_commit=$(git -C "$kernel_source" rev-parse HEAD)
|
||||
kernel_subject=$(git -C "$kernel_source" show -s --format=%s HEAD)
|
||||
kernel_dirty=no
|
||||
if [[ -n $(git -C "$kernel_source" status --porcelain --untracked-files=normal) ]]; then
|
||||
kernel_dirty=yes
|
||||
fi
|
||||
kernel_sha256=$(sha256sum "$kernel_image" | awk '{print $1}')
|
||||
config_sha256=$(sha256sum "$kernel_config" | awk '{print $1}')
|
||||
vng_version=$(vng --version | head -n 1)
|
||||
|
||||
printf '%s\n' \
|
||||
'KVM preflight: OK' \
|
||||
"benchmark_root=$benchmark_root" \
|
||||
"kernel_image=$kernel_image" \
|
||||
"kernel_release=$kernel_release" \
|
||||
"kernel_sha256=$kernel_sha256" \
|
||||
"kernel_config_sha256=$config_sha256" \
|
||||
"kernel_source_commit=$kernel_commit" \
|
||||
"kernel_source_dirty=$kernel_dirty" \
|
||||
"kernel_source_subject=$kernel_subject" \
|
||||
"vng_version=$vng_version"
|
||||
|
||||
if $check_only; then
|
||||
(($# == 0)) || die "--check does not accept a guest command"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
(($# > 0)) || die "missing guest command; place it after --"
|
||||
[[ -d $cwd ]] || die "guest working directory does not exist: $cwd"
|
||||
cwd=$(realpath -e -- "$cwd")
|
||||
|
||||
declare -a normalized_rwdirs=()
|
||||
cwd_is_rw=false
|
||||
for path in "${rwdirs[@]}"; do
|
||||
[[ -d $path ]] || die "--rwdir is not a directory: $path"
|
||||
path=$(realpath -e -- "$path")
|
||||
normalized_rwdirs+=("$path")
|
||||
if [[ $path == "$cwd" ]]; then
|
||||
cwd_is_rw=true
|
||||
fi
|
||||
done
|
||||
|
||||
quote_for_posix_shell "$kernel_release"
|
||||
quoted_release=$REPLY
|
||||
quote_for_posix_shell "$cwd"
|
||||
quoted_cwd=$REPLY
|
||||
quoted_command=
|
||||
for argument in "$@"; do
|
||||
quote_for_posix_shell "$argument"
|
||||
quoted_command+="$REPLY "
|
||||
done
|
||||
|
||||
guest_script=$'#!/bin/sh\nset -eu\n'
|
||||
guest_script+=$'mount_if_needed() {\n'
|
||||
guest_script+=$' source=$1 type=$2 target=$3\n'
|
||||
guest_script+=$' mkdir -p -- "$target"\n'
|
||||
guest_script+=$' grep -qs " $target " /proc/mounts || mount -t "$type" "$source" "$target"\n'
|
||||
guest_script+=$'}\n'
|
||||
guest_script+=$'mount_if_needed bpf bpf /sys/fs/bpf\n'
|
||||
guest_script+=$'mount_if_needed debugfs debugfs /sys/kernel/debug\n'
|
||||
guest_script+=$'mount_if_needed tracefs tracefs /sys/kernel/tracing\n'
|
||||
guest_script+=$'mount_if_needed none cgroup2 /sys/fs/cgroup\n'
|
||||
guest_script+=$'ulimit -l unlimited\n'
|
||||
guest_script+="expected_release=$quoted_release"$'\n'
|
||||
guest_script+=$'actual_release=$(uname -r)\n'
|
||||
guest_script+=$'if [ "$actual_release" != "$expected_release" ]; then\n'
|
||||
guest_script+=$' echo "ERROR: guest kernel mismatch: expected=$expected_release actual=$actual_release" >&2\n'
|
||||
guest_script+=$' exit 1\n'
|
||||
guest_script+=$'fi\n'
|
||||
guest_script+=$'printf "guest_kernel=%s\\nguest_identity=%s\\n" "$actual_release" "$(id)"\n'
|
||||
guest_script+="cd -- $quoted_cwd"$'\n'
|
||||
guest_script+="exec $quoted_command"$'\n'
|
||||
|
||||
lock_file=${TMPDIR:-/tmp}/test-bpf-tutorial-kvm.lock
|
||||
exec {lock_fd}>"$lock_file"
|
||||
flock -w 5 "$lock_fd" || die "another tutorial KVM run holds $lock_file"
|
||||
|
||||
host_script_dir=$(mktemp -d /tmp/test-bpf-tutorial-kvm.XXXXXX)
|
||||
host_script_path=$host_script_dir/guest-command.sh
|
||||
guest_script_dir=/tmp/${host_script_dir##*/}
|
||||
guest_script_path=$guest_script_dir/guest-command.sh
|
||||
cleanup() {
|
||||
rm -f -- "$host_script_path"
|
||||
rmdir -- "$host_script_dir" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
printf '%s' "$guest_script" >"$host_script_path"
|
||||
chmod 0555 "$host_script_path"
|
||||
|
||||
declare -a vng_command=(
|
||||
vng
|
||||
--run "$kernel_image"
|
||||
--cwd "$cwd"
|
||||
--disable-monitor
|
||||
--cpus "$cpus"
|
||||
--memory "$memory"
|
||||
--overlay-rwdir /tmp
|
||||
'--qemu-opts=-machine accel=kvm'
|
||||
--append 'loglevel=4 panic=30 oops=panic'
|
||||
--rodir "$guest_script_dir=$host_script_dir"
|
||||
--exec "$guest_script_path"
|
||||
)
|
||||
|
||||
if ! $cwd_is_rw; then
|
||||
vng_command+=(--rodir "$cwd")
|
||||
fi
|
||||
for path in "${normalized_rwdirs[@]}"; do
|
||||
vng_command+=(--rwdir "$path")
|
||||
done
|
||||
if [[ $network != none ]]; then
|
||||
vng_command+=(--network "$network")
|
||||
fi
|
||||
for args in "${extra_append[@]}"; do
|
||||
vng_command+=(--append "$args")
|
||||
done
|
||||
if $verbose; then
|
||||
vng_command+=(--verbose)
|
||||
else
|
||||
vng_command+=(--quiet)
|
||||
fi
|
||||
if $dry_run; then
|
||||
vng_command+=(--dry-run)
|
||||
fi
|
||||
|
||||
printf 'guest_cwd=%s\n' "$cwd"
|
||||
printf 'guest_network=%s\n' "$network"
|
||||
printf 'guest_command='
|
||||
printf '%q ' "$@"
|
||||
printf '\n'
|
||||
if ((${#normalized_rwdirs[@]})); then
|
||||
printf 'WARNING: guest root has host write access to:' >&2
|
||||
printf ' %q' "${normalized_rwdirs[@]}" >&2
|
||||
printf '\n' >&2
|
||||
fi
|
||||
|
||||
if $dry_run; then
|
||||
"${vng_command[@]}"
|
||||
else
|
||||
timeout --foreground --signal=TERM --kill-after=10s "${wall_timeout}s" \
|
||||
"${vng_command[@]}"
|
||||
fi
|
||||
Reference in New Issue
Block a user