diff --git a/README.md b/README.md index 2ec62b7..81bedf9 100644 --- a/README.md +++ b/README.md @@ -24,5 +24,5 @@ cargo build ## Run ```bash -sudo target/debug/{{project-name}} --path target/bpfel-unknown-none/debug/{{project-name}} +cargo xtask run -p ``` diff --git a/xtask/src/build_ebpf.rs b/xtask/src/build_ebpf.rs index 21d8220..85facdc 100644 --- a/xtask/src/build_ebpf.rs +++ b/xtask/src/build_ebpf.rs @@ -32,13 +32,15 @@ impl std::fmt::Display for Architecture { #[derive(StructOpt)] pub struct Options { + /// Set the endianness of the BPF target #[structopt(default_value = "bpfel-unknown-none", long)] - target: Architecture, + pub target: Architecture, + /// Build the release target #[structopt(long)] - release: bool, + pub release: bool, } -pub fn build(opts: Options) -> Result<(), anyhow::Error> { +pub fn build_ebpf(opts: Options) -> Result<(), anyhow::Error> { let dir = PathBuf::from("{{project-name}}-ebpf"); let target = format!("--target={}", opts.target); let mut args = vec![ @@ -56,7 +58,7 @@ pub fn build(opts: Options) -> Result<(), anyhow::Error> { .current_dir(&dir) .args(&args) .status() - .expect("failed to build bpf examples"); + .expect("failed to build bpf program"); assert!(status.success()); Ok(()) } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 96308fd..9dedae1 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,4 +1,5 @@ mod build_ebpf; +mod run; use std::process::exit; @@ -12,6 +13,7 @@ pub struct Options { #[derive(StructOpt)] enum Command { BuildEbpf(build_ebpf::Options), + Run(run::Options), } fn main() { @@ -19,7 +21,8 @@ fn main() { use Command::*; let ret = match opts.command { - BuildEbpf(opts) => build_ebpf::build(opts), + BuildEbpf(opts) => build_ebpf::build_ebpf(opts), + Run(opts) => run::run(opts), }; if let Err(e) = ret { diff --git a/xtask/src/run.rs b/xtask/src/run.rs new file mode 100644 index 0000000..63474ee --- /dev/null +++ b/xtask/src/run.rs @@ -0,0 +1,75 @@ +use std::{os::unix::process::CommandExt, process::Command}; + +use anyhow::Context as _; +use structopt::StructOpt; + +use crate::build_ebpf::{build_ebpf, Architecture, Options as BuildOptions}; + +#[derive(StructOpt)] +pub struct Options { + /// Set the endianness of the BPF target + #[structopt(default_value = "bpfel-unknown-none", long)] + pub bpf_target: Architecture, + /// Build and run the release target + #[structopt(long)] + pub release: bool, + /// The command used to wrap your application + #[structopt(short, long, default_value = "sudo -E")] + pub runner: String, + /// A convenience flag that will supply `--path /path/to/bpf/object` to your application + #[structopt(short = "p", long)] + pub supply_path: bool, + /// Arguments to pass to your application + #[structopt(name = "args", last = true)] + pub run_args: Vec, +} + +/// Build the project +fn build(opts: &Options) -> Result<(), anyhow::Error> { + let mut args = vec!["build"]; + if opts.release { + args.push("--release") + } + let status = Command::new("cargo") + .args(&args) + .status() + .expect("failed to build userspace"); + assert!(status.success()); + Ok(()) +} + +/// Build and run the project +pub fn run(opts: Options) -> Result<(), anyhow::Error> { + // build our ebpf program followed by our application + build_ebpf(BuildOptions { + target: opts.bpf_target, + release: opts.release, + }) + .context("Error while building eBPF program")?; + build(&opts).context("Error while building userspace application")?; + + // profile we are building (release or debug) + let profile = if opts.release { "release" } else { "debug" }; + let bin_path = format!("target/{}/{{project-name}}", profile); + let bpf_path = format!("target/{}/{}/{{project-name}}", opts.bpf_target, profile); + + // arguments to pass to the application + let mut run_args: Vec<_> = opts.run_args.iter().map(String::as_str).collect(); + if opts.supply_path { + run_args.push("--path"); + run_args.push(bpf_path.as_str()); + }; + + // configure args + let mut args: Vec<_> = opts.runner.trim().split_terminator(" ").collect(); + args.push(bin_path.as_str()); + args.append(&mut run_args); + + // spawn the command + let err = Command::new(args.get(0).expect("No first argument")) + .args(args.iter().skip(1)) + .exec(); + + // we shouldn't get here unless the command failed to spawn + Err(anyhow::Error::from(err).context(format!("Failed to run `{}`", args.join(" ")))) +}