use std::fs;
use std::path;
use std::process;
use std::str;
pub mod config;
mod fetch;
fn is_module_supported(data_dir: &str, module: &str) -> bool {
let path = format!("{}/modules/{}", data_dir, module);
path::Path::new(&path).is_dir()
}
fn get_build_image_identifier(kernel_version: &str) -> String {
format!("{}-builder:{}-{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), kernel_version)
}
fn get_runtime_image_identifier(kernel_version: &str) -> String {
format!("{}-runtime:{}-{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), kernel_version)
}
fn get_module_image_identifier(module: &str, module_version: &str, kernel_version: &str) -> String {
format!("{}-{}:{}-{}", env!("CARGO_PKG_NAME"), module, module_version, kernel_version)
}
fn image_exists(identifier: &str) -> bool {
process::Command::new("podman")
.args(["image", "exists", identifier])
.status()
.expect("Error while checking for pre-existing image")
.success()
}
pub fn build(config: &config::Config, module: &config::ModuleConfig, idempotent: bool, no_prune: bool) {
if !is_module_supported(&config.data_dir, &module.name) {
panic!("Module {} is not supported", module.name);
}
let kernel_version = fetch::kernel_version();
let arch = fetch::architecture();
let podmod_version = env!("CARGO_PKG_VERSION");
let build_image_name = get_build_image_identifier(&kernel_version);
let runtime_image_name = get_runtime_image_identifier(&kernel_version);
let module_image_name = get_module_image_identifier(&module.name, &module.version, &kernel_version);
if image_exists(&module_image_name) {
if idempotent {
return;
}
panic!("Module {} is already built", module.name);
}
if !image_exists(&build_image_name) {
println!("Building builder image for kernel version {} ...", kernel_version);
process::Command::new("podman")
.args(["build", "-t", &build_image_name])
.args(["--build-arg", &format!("ARCH={}", arch)])
.args(["--build-arg", &format!("KERNEL_VERSION={}", kernel_version)])
.args(["--file", "Builder.containerfile"])
.arg(format!("{}/common/", config.data_dir))
.status()
.expect("Error while building the builder image");
}
if !image_exists(&runtime_image_name) {
println!("Building runtime image for kernel version {} ...", kernel_version);
process::Command::new("podman")
.args(["build", "-t", &runtime_image_name])
.args(["--build-arg", &format!("KERNEL_VERSION={}", kernel_version)])
.args(["--build-arg", &format!("PODMOD_VERSION={}", podmod_version)])
.args(["--file", "Runtime.containerfile"])
.arg(format!("{}/common/", config.data_dir))
.status()
.expect("Error while building the runtime image");
}
println!("Building module {} for kernel version {} ...", module.name, kernel_version);
let mut command = process::Command::new("podman");
command
.args(["build", "-t", &module_image_name])
.args(["--build-arg", &format!("ARCH={}", arch)])
.args(["--build-arg", &format!("KERNEL_VERSION={}", kernel_version)])
.args(["--build-arg", &format!("MODULE_VERSION={}", module.version)])
.args(["--build-arg", &format!("PODMOD_VERSION={}", podmod_version)]);
for (key, value) in &module.build_args {
command.args(["--build-arg", &format!("{}={}", key, value)]);
}
command
.arg(format!("{}/modules/{}", &config.data_dir, module.name))
.status()
.expect("Error while building the kernel module");
if !no_prune {
process::Command::new("podman")
.args(["system", "prune", "-f"])
.status()
.expect("Error while pruning intermediary images");
}
}
pub fn load(module: &config::ModuleConfig, idempotent: bool) {
if fetch::is_module_loaded(&module.name) {
if idempotent {
return;
}
panic!("Module {} is already loaded", module.name);
}
println!("Loading module {} ...", module.name);
let mut command = vec![String::from("load")];
command.extend(module.kernel_args.clone());
run(module, &command);
}
pub fn modules(config: &config::Config) {
println!("The following kernel modules are supported:");
let modules = fs::read_dir(format!("{}/modules", config.data_dir))
.expect("Error while reading data directory");
for module in modules {
let module = module.unwrap().path();
let module = module.file_name().unwrap();
let module = module.to_str().unwrap();
println!("{}", module);
}
}
pub fn run(module: &config::ModuleConfig, command: &Vec<String>) {
let kernel_version = fetch::kernel_version();
let image_name = get_module_image_identifier(&module.name, &module.version, &kernel_version);
if !image_exists(&image_name) {
panic!("Module {} is not built", module.name);
}
println!("Executing command {:?}, in module {} ...", command, module.name);
process::Command::new("podman")
.args(["run", "--rm", "--privileged"])
.args(&module.container_args)
.arg(&image_name)
.args(command)
.status()
.expect("Error while loading the kernel module");
}
pub fn shell(module: &config::ModuleConfig, shell: &str) {
let mut module = module.clone();
module.container_args.extend(vec![String::from("-it")]);
println!("Starting shell session in module {} ...", module.name);
run(&module, &vec![String::from(shell)]);
}
pub fn unload(module: &config::ModuleConfig, idempotent: bool) {
if !fetch::is_module_loaded(&module.name) {
if idempotent {
return;
}
panic!("Module {} is not loaded", module.name);
}
let kernel_version = fetch::kernel_version();
let image_name = get_module_image_identifier(&module.name, &module.version, &kernel_version);
println!("Unloading module {} ...", module.name);
process::Command::new("podman")
.args(["run", "--rm", "--privileged", &image_name, "unload"])
.status()
.expect("Error while unloading the kernel module");
}