1use std::fs;
17use std::path;
18use std::process;
19use std::str;
20
21pub mod config;
22mod fetch;
23
24fn is_module_supported(data_dir: &str, module: &str) -> bool {
25 let path = format!("{}/modules/{}", data_dir, module);
27 path::Path::new(&path).is_dir()
28}
29
30fn get_build_image_identifier(kernel_version: &str) -> String {
31 format!("{}-builder:{}-{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), kernel_version)
32}
33
34fn get_runtime_image_identifier(kernel_version: &str) -> String {
35 format!("{}-runtime:{}-{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), kernel_version)
36}
37
38fn get_module_image_identifier(module: &str, module_version: &str, kernel_version: &str) -> String {
39 format!("{}-{}:{}-{}", env!("CARGO_PKG_NAME"), module, module_version, kernel_version)
40}
41
42fn image_exists(identifier: &str) -> bool {
43 process::Command::new("podman")
46 .args(["image", "exists", identifier])
47 .status()
48 .expect("Error while checking for pre-existing image")
49 .success()
50}
51
52pub fn build(config: &config::Config, module: &config::ModuleConfig, idempotent: bool, no_prune: bool) {
53 if !is_module_supported(&config.data_dir, &module.name) {
55 panic!("Module {} is not supported", module.name);
56 }
57
58 let kernel_version = fetch::kernel_version();
61 let arch = fetch::architecture();
62 let podmod_version = env!("CARGO_PKG_VERSION");
63
64 let build_image_name = get_build_image_identifier(&kernel_version);
65 let runtime_image_name = get_runtime_image_identifier(&kernel_version);
66 let module_image_name = get_module_image_identifier(&module.name, &module.version, &kernel_version);
67
68 if image_exists(&module_image_name) {
70 if idempotent {
71 return;
72 }
73
74 panic!("Module {} is already built", module.name);
75 }
76
77 if !image_exists(&build_image_name) {
79 println!("Building builder image for kernel version {} ...", kernel_version);
80
81 process::Command::new("podman")
82 .args(["build", "-t", &build_image_name])
83 .args(["--build-arg", &format!("ARCH={}", arch)])
84 .args(["--build-arg", &format!("KERNEL_VERSION={}", kernel_version)])
85 .args(["--file", "Builder.containerfile"])
86 .arg(format!("{}/common/", config.data_dir))
87 .status()
88 .expect("Error while building the builder image");
89 }
90
91 if !image_exists(&runtime_image_name) {
93 println!("Building runtime image for kernel version {} ...", kernel_version);
94
95 process::Command::new("podman")
96 .args(["build", "-t", &runtime_image_name])
97 .args(["--build-arg", &format!("KERNEL_VERSION={}", kernel_version)])
98 .args(["--build-arg", &format!("PODMOD_VERSION={}", podmod_version)])
99 .args(["--file", "Runtime.containerfile"])
100 .arg(format!("{}/common/", config.data_dir))
101 .status()
102 .expect("Error while building the runtime image");
103 }
104
105 println!("Building module {} for kernel version {} ...", module.name, kernel_version);
106
107 let mut command = process::Command::new("podman");
110
111 command
112 .args(["build", "-t", &module_image_name])
113 .args(["--build-arg", &format!("ARCH={}", arch)])
114 .args(["--build-arg", &format!("KERNEL_VERSION={}", kernel_version)])
115 .args(["--build-arg", &format!("MODULE_VERSION={}", module.version)])
116 .args(["--build-arg", &format!("PODMOD_VERSION={}", podmod_version)]);
117
118 for (key, value) in &module.build_args {
120 command.args(["--build-arg", &format!("{}={}", key, value)]);
121 }
122
123 command
124 .arg(format!("{}/modules/{}", &config.data_dir, module.name))
125 .status()
126 .expect("Error while building the kernel module");
127
128 if !no_prune {
132 process::Command::new("podman")
133 .args(["system", "prune", "-f"])
134 .status()
135 .expect("Error while pruning intermediary images");
136 }
137}
138
139pub fn load(module: &config::ModuleConfig, idempotent: bool) {
140 if fetch::is_module_loaded(&module.name) {
142 if idempotent {
143 return;
144 }
145
146 panic!("Module {} is already loaded", module.name);
147 }
148
149 println!("Loading module {} ...", module.name);
150
151 let mut command = vec![String::from("load")];
152 command.extend(module.kernel_args.clone());
153
154 run(module, &command);
157}
158
159pub fn modules(config: &config::Config) {
160 println!("The following kernel modules are supported:");
161
162 let modules = fs::read_dir(format!("{}/modules", config.data_dir))
164 .expect("Error while reading data directory");
165
166 for module in modules {
167 let module = module.unwrap().path();
169 let module = module.file_name().unwrap();
170 let module = module.to_str().unwrap();
171 println!("{}", module);
172 }
173}
174
175pub fn run(module: &config::ModuleConfig, command: &Vec<String>) {
176 let kernel_version = fetch::kernel_version();
178 let image_name = get_module_image_identifier(&module.name, &module.version, &kernel_version);
179
180 if !image_exists(&image_name) {
182 panic!("Module {} is not built", module.name);
183 }
184
185 println!("Executing command {:?}, in module {} ...", command, module.name);
186
187 process::Command::new("podman")
190 .args(["run", "--rm", "--privileged"])
191 .args(&module.container_args)
192 .arg(&image_name)
193 .args(command)
194 .status()
195 .expect("Error while loading the kernel module");
196}
197
198pub fn shell(module: &config::ModuleConfig, shell: &str) {
199 let mut module = module.clone();
200 module.container_args.extend(vec![String::from("-it")]);
201
202 println!("Starting shell session in module {} ...", module.name);
203
204 run(&module, &vec![String::from(shell)]);
207}
208
209pub fn unload(module: &config::ModuleConfig, idempotent: bool) {
210 if !fetch::is_module_loaded(&module.name) {
212 if idempotent {
213 return;
214 }
215
216 panic!("Module {} is not loaded", module.name);
217 }
218
219 let kernel_version = fetch::kernel_version();
221 let image_name = get_module_image_identifier(&module.name, &module.version, &kernel_version);
222
223 println!("Unloading module {} ...", module.name);
224
225 process::Command::new("podman")
227 .args(["run", "--rm", "--privileged", &image_name, "unload"])
228 .status()
229 .expect("Error while unloading the kernel module");
230}