11use crate :: helpers:: {
22 CheckGroup , CheckGroupResult , CheckResult ,
3- CheckResultValue :: { Errored , Failed , Passed } ,
3+ CheckResultValue :: { Errored , Failed , Passed } , host_executor :: HostNamespaceExecutor ,
44} ;
55
66use anyhow:: { Result , bail} ;
77use async_trait:: async_trait;
88use log:: debug;
99use procfs:: { Current , sys:: kernel} ;
10- use std:: fs;
11- use std:: path:: PathBuf ;
12- use std:: process:: Command ;
10+ use std:: { fs, path:: PathBuf , process:: Command } ;
11+ use futures:: { future:: join_all, FutureExt } ;
1312
1413const GROUP_IDENTIFIER : & str = "KernelChecks" ;
1514const NAME : & str = "Kernel Checks" ;
15+ // TODO (bml) assemble actual list
16+ const REQUIRED_MODULES : & [ & str ] = & [
17+ "nf_tables" ,
18+ ] ;
1619
17- pub struct KernelChecks ;
20+ pub struct KernelChecks {
21+ host_executor : HostNamespaceExecutor ,
22+ }
1823
1924impl KernelChecks {
20- pub fn run_all ( & self ) -> CheckGroupResult {
21- let results = vec ! [ self . has_modules( ) , self . version_is_good( ) ] ;
25+ pub fn new ( host_executor : HostNamespaceExecutor ) -> Self {
26+ KernelChecks { host_executor }
27+ }
28+
29+ /// Run all the checkers asynchronously, then
30+ /// join and collect the results.
31+ pub async fn run_all ( & self ) -> CheckGroupResult {
32+ let results = join_all ( [
33+ self . has_modules ( ) . boxed ( ) ,
34+ self . version_is_good ( ) . boxed ( ) ,
35+ ] )
36+ . await ;
37+
2238
2339 let mut group_result = Passed ;
2440 for res in results. iter ( ) {
@@ -39,27 +55,15 @@ impl KernelChecks {
3955 }
4056 }
4157
42- fn current_kernel_version ( & self ) -> Result < String > {
43- let output = Command :: new ( "uname" )
44- . arg ( "-r" )
45- . output ( )
46- . expect ( "Failed to execute command" ) ;
47-
48- if !output. status . success ( ) {
49- let error_message = String :: from_utf8_lossy ( & output. stderr ) ;
50- bail ! ( "{}" , error_message. to_string( ) ) ;
51- }
52-
53- let kernel_version = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
54- Ok ( kernel_version)
55- }
56-
57- fn version_is_good ( & self ) -> CheckResult {
58+ async fn version_is_good ( & self ) -> CheckResult {
5859 let name = String :: from ( "Host Kernel Version Is Good" ) ;
5960 let mut result = Passed ;
6061
61- // Get kernel version
62- let current = kernel:: Version :: current ( ) ;
62+ // Get host kernel version
63+ let current = self . host_executor . spawn_in_host_ns ( async {
64+ kernel:: Version :: current ( )
65+ } ) . await . expect ( "error spawning in host" ) ;
66+
6367 if let Err ( e) = current {
6468 return CheckResult :: new ( & name, Errored ( e. to_string ( ) ) ) ;
6569 }
@@ -72,68 +76,90 @@ impl KernelChecks {
7276 CheckResult :: new ( & name, result)
7377 }
7478
75- fn has_modules ( & self ) -> CheckResult {
79+ async fn has_modules ( & self ) -> CheckResult {
7680 let name = String :: from ( "Host Has Necessary Modules" ) ;
7781 let mut result = Passed ;
7882
79- let mut required_modules = vec ! [
80- "xen_evtchn" ,
81- "xen-privcmd" ,
82- "xen-netback" ,
83- "xen-pciback" ,
84- "xen-blkback" ,
85- "xen-gntdev" ,
86- "xen-gntalloc" ,
87- ] ;
88-
89- // Get kernel version
90- let kernel_version = self . current_kernel_version ( ) ;
91- if let Err ( e) = kernel_version {
92- return CheckResult :: new ( & name, Errored ( e. to_string ( ) ) ) ;
93- }
94- let kernel_version = kernel_version. unwrap ( ) ;
83+ let required_modules: Vec < String > = REQUIRED_MODULES . iter ( ) . map ( |s| s. to_string ( ) ) . collect ( ) ;
9584
9685 // Search builtin modules
97- let path = PathBuf :: from ( format ! ( "/lib/modules/{kernel_version}/modules.builtin" ) ) ;
98- let builtins = fs:: read_to_string ( path) ;
99- if let Err ( e) = builtins {
100- return CheckResult :: new ( & name, Errored ( e. to_string ( ) ) ) ;
86+ let remaining = match self . find_builtins ( & required_modules) . await {
87+ Ok ( r) => r,
88+ Err ( e) => return CheckResult :: new ( & name, Errored ( format ! ( "getting kernel builtins {e}" ) ) ) ,
89+ } ;
90+
91+ // Search loaded modules
92+ let remaining = match self . find_loaded ( & remaining) . await {
93+ Ok ( r) => r,
94+ Err ( e) => return CheckResult :: new ( & name, Errored ( format ! ( "getting kernel modules {e}" ) ) ) ,
95+ } ;
96+ if !remaining. is_empty ( ) {
97+ result = Failed ( format ! ( "missing {:?}" , remaining) )
10198 }
102- let builtins = builtins. unwrap ( ) ;
99+
100+ CheckResult :: new ( & name, result)
101+ }
102+
103+ /// Looks at builtins for kernel_version and compares that to the list of
104+ /// required modules.
105+ /// Returns a vec of everything from required_modules that WAS NOT found in builtins.
106+ async fn find_builtins ( & self , required_modules : & Vec < String > ) -> Result < Vec < String > > {
107+ let mut modules_to_find: Vec < String > = required_modules. clone ( ) ;
108+
109+ // read host builtins
110+ let builtins = self . host_executor . spawn_in_host_ns ( async move {
111+ // Get kernel version
112+ let output = Command :: new ( "uname" )
113+ . arg ( "-r" )
114+ . output ( ) ?;
115+
116+ if !output. status . success ( ) {
117+ let error_message = String :: from_utf8_lossy ( & output. stderr ) ;
118+ bail ! ( "{}" , error_message. to_string( ) ) ;
119+ }
120+ let kernel_version = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
121+ let path = PathBuf :: from ( format ! ( "/lib/modules/{kernel_version}/modules.builtin" ) ) ;
122+ fs:: read_to_string ( path) . map_err ( |e| anyhow:: anyhow!( e) )
123+ } ) . await ??;
103124
104125 for builtin in builtins. lines ( ) {
105- let found = required_modules
126+ let found = modules_to_find
106127 . iter ( )
107128 . position ( |required| builtin. contains ( required) ) ;
108129
109130 if let Some ( index) = found {
110- debug ! ( "builtin {}" , required_modules [ index] ) ;
111- required_modules . remove ( index) ;
131+ debug ! ( "builtin {}" , modules_to_find [ index] ) ;
132+ modules_to_find . remove ( index) ;
112133 }
113134 }
114135
115- // Search loaded modules
116- let modules = procfs:: KernelModules :: current ( ) ;
117- if let Err ( e) = modules {
118- return CheckResult :: new ( & name, Errored ( format ! ( "getting kernel modules {e}" ) ) ) ;
119- }
136+ Ok ( modules_to_find)
137+ }
138+
139+ /// Looks at loaded modules for the current host kernel and compares that to the list of
140+ /// required modules.
141+ /// Returns a vec of everything from required_modules that WAS NOT loaded.
142+ async fn find_loaded ( & self , required_modules : & Vec < String > ) -> Result < Vec < String > > {
143+ let mut modules_to_find: Vec < String > = required_modules. clone ( ) ;
144+
145+ let modules = self . host_executor . spawn_in_host_ns ( async move {
146+ procfs:: KernelModules :: current ( )
147+ } ) . await ?;
148+
120149 let modules = modules. unwrap ( ) ;
121150
122151 for ( name, _) in modules. 0 . iter ( ) {
123- let found = required_modules
152+ let found = modules_to_find
124153 . iter ( )
125154 . position ( |required| required == name) ;
126155
127156 if let Some ( index) = found {
128- debug ! ( "module {}" , required_modules [ index] ) ;
129- required_modules . remove ( index) ;
157+ debug ! ( "module {}" , modules_to_find [ index] ) ;
158+ modules_to_find . remove ( index) ;
130159 }
131160 }
132- if !required_modules. is_empty ( ) {
133- result = Failed ( format ! ( "missing {:?}" , required_modules) )
134- }
135161
136- CheckResult :: new ( & name , result )
162+ Ok ( modules_to_find )
137163 }
138164}
139165
@@ -152,6 +178,6 @@ impl CheckGroup for KernelChecks {
152178 }
153179
154180 async fn run ( & self ) -> CheckGroupResult {
155- self . run_all ( )
181+ self . run_all ( ) . await
156182 }
157183}
0 commit comments