11// SPDX-FileCopyrightText: 2024 The rsinit Authors
22// SPDX-License-Identifier: GPL-2.0-only
33
4+ use std:: fmt:: Debug ;
5+
46use nix:: mount:: MsFlags ;
57
6- use crate :: util:: { read_file, Result } ;
8+ use crate :: {
9+ init:: CmdlineCallback ,
10+ util:: { read_file, Result } ,
11+ } ;
712
813#[ derive( Debug , PartialEq ) ]
9- pub struct CmdlineOptions {
14+ pub struct CmdlineOptions < ' a > {
1015 pub root : Option < String > ,
1116 pub rootfstype : Option < String > ,
1217 pub rootflags : Option < String > ,
1318 pub rootfsflags : MsFlags ,
1419 pub nfsroot : Option < String > ,
1520 pub init : String ,
1621 pub cleanup : bool ,
22+ callbacks : CmdlineOptionsCallbacks < ' a > ,
23+ }
24+
25+ #[ derive( Default ) ]
26+ struct CmdlineOptionsCallbacks < ' a > ( Vec < Box < CmdlineCallback < ' a > > > ) ;
27+
28+ impl PartialEq for CmdlineOptionsCallbacks < ' _ > {
29+ fn eq ( & self , _other : & Self ) -> bool {
30+ true
31+ }
32+ }
33+
34+ impl Debug for CmdlineOptionsCallbacks < ' _ > {
35+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
36+ f. debug_struct ( "CmdlineOptionsCallbacks" )
37+ . field ( "callbacks_count" , & self . 0 . len ( ) )
38+ . finish ( )
39+ }
1740}
1841
1942const SBIN_INIT : & str = "/sbin/init" ;
2043
21- impl Default for CmdlineOptions {
22- fn default ( ) -> CmdlineOptions {
44+ impl < ' a > Default for CmdlineOptions < ' a > {
45+ fn default ( ) -> CmdlineOptions < ' a > {
2346 CmdlineOptions {
2447 root : None ,
2548 rootfstype : None ,
@@ -28,15 +51,21 @@ impl Default for CmdlineOptions {
2851 nfsroot : None ,
2952 init : SBIN_INIT . into ( ) ,
3053 cleanup : true ,
54+ callbacks : CmdlineOptionsCallbacks :: default ( ) ,
3155 }
3256 }
3357}
3458
35- fn ensure_value < ' a > ( key : & str , value : Option < & ' a str > ) -> Result < & ' a str > {
59+ pub fn ensure_value < ' a > ( key : & str , value : Option < & ' a str > ) -> Result < & ' a str > {
3660 value. ok_or ( format ! ( "Cmdline option '{key}' must have an argument!" ) . into ( ) )
3761}
3862
39- fn parse_option ( key : & str , value : Option < & str > , options : & mut CmdlineOptions ) -> Result < ( ) > {
63+ fn parse_option < ' a > (
64+ key : & str ,
65+ value : Option < & str > ,
66+ options : & mut CmdlineOptions ,
67+ callbacks : & mut [ Box < CmdlineCallback < ' a > > ] ,
68+ ) -> Result < ( ) > {
4069 match key {
4170 "root" => options. root = Some ( ensure_value ( key, value) ?. to_string ( ) ) ,
4271 "rootfstype" => options. rootfstype = Some ( ensure_value ( key, value) ?. to_string ( ) ) ,
@@ -45,7 +74,11 @@ fn parse_option(key: &str, value: Option<&str>, options: &mut CmdlineOptions) ->
4574 "rw" => options. rootfsflags . remove ( MsFlags :: MS_RDONLY ) ,
4675 "nfsroot" => options. nfsroot = Some ( ensure_value ( key, value) ?. to_string ( ) ) ,
4776 "init" => options. init = ensure_value ( key, value) ?. into ( ) ,
48- _ => ( ) ,
77+ _ => {
78+ for cb in callbacks {
79+ cb ( key, value) ?
80+ }
81+ }
4982 }
5083 Ok ( ( ) )
5184}
@@ -91,8 +124,24 @@ fn parse_nfsroot(options: &mut CmdlineOptions) -> Result<()> {
91124 Ok ( ( ) )
92125}
93126
94- impl CmdlineOptions {
95- pub fn from_string ( cmdline : & str ) -> Result < Self > {
127+ impl < ' a > CmdlineOptions < ' a > {
128+ pub fn new ( ) -> Self {
129+ Self :: default ( )
130+ }
131+
132+ pub fn new_with_callbacks ( callbacks : Vec < Box < CmdlineCallback < ' a > > > ) -> Self {
133+ Self {
134+ callbacks : CmdlineOptionsCallbacks ( callbacks) ,
135+ ..Default :: default ( )
136+ }
137+ }
138+
139+ pub fn from_file ( & mut self , path : & str ) -> Result < Self > {
140+ let cmdline = read_file ( path) ?;
141+ self . from_string ( & cmdline)
142+ }
143+
144+ pub fn from_string ( & mut self , cmdline : & str ) -> Result < Self > {
96145 let mut options = Self :: default ( ) ;
97146 let mut have_value = false ;
98147 let mut quoted = false ;
@@ -128,6 +177,7 @@ impl CmdlineOptions {
128177 None
129178 } ,
130179 & mut options,
180+ & mut self . callbacks . 0 ,
131181 ) ?;
132182 }
133183 key = & cmdline[ 0 ..0 ] ;
@@ -150,15 +200,12 @@ impl CmdlineOptions {
150200
151201 Ok ( options)
152202 }
153-
154- pub fn from_file ( filename : & str ) -> Result < Self > {
155- let cmdline = read_file ( filename) ?;
156- Self :: from_string ( & cmdline)
157- }
158203}
159204
160205#[ cfg( test) ]
161206mod tests {
207+ use std:: cell:: RefCell ;
208+
162209 use super :: * ;
163210
164211 #[ test]
@@ -171,7 +218,7 @@ mod tests {
171218 ..Default :: default ( )
172219 } ;
173220
174- let options = CmdlineOptions :: from_string ( cmdline) . expect ( "failed" ) ;
221+ let options = CmdlineOptions :: new ( ) . from_string ( cmdline) . expect ( "failed" ) ;
175222
176223 assert_eq ! ( options, expected) ;
177224 }
@@ -189,7 +236,7 @@ mod tests {
189236 ..Default :: default ( )
190237 } ;
191238
192- let options = CmdlineOptions :: from_string ( cmdline) . expect ( "failed" ) ;
239+ let options = CmdlineOptions :: new ( ) . from_string ( cmdline) . expect ( "failed" ) ;
193240
194241 assert_eq ! ( options, expected) ;
195242 }
@@ -206,7 +253,7 @@ mod tests {
206253 ..Default :: default ( )
207254 } ;
208255
209- let options = CmdlineOptions :: from_string ( cmdline) . expect ( "failed" ) ;
256+ let options = CmdlineOptions :: new ( ) . from_string ( cmdline) . expect ( "failed" ) ;
210257
211258 assert_eq ! ( options, expected) ;
212259 }
@@ -226,7 +273,7 @@ mod tests {
226273 ..Default :: default ( )
227274 } ;
228275
229- let options = CmdlineOptions :: from_string ( cmdline) . expect ( "failed" ) ;
276+ let options = CmdlineOptions :: new ( ) . from_string ( cmdline) . expect ( "failed" ) ;
230277
231278 assert_eq ! ( options, expected) ;
232279 }
@@ -241,8 +288,36 @@ mod tests {
241288 ..Default :: default ( )
242289 } ;
243290
244- let options = CmdlineOptions :: from_string ( cmdline) . expect ( "failed" ) ;
291+ let options = CmdlineOptions :: new ( ) . from_string ( cmdline) . expect ( "failed" ) ;
292+
293+ assert_eq ! ( options, expected) ;
294+ }
295+
296+ #[ test]
297+ fn test_callbacks ( ) {
298+ let cmdline = "root=/dev/mmcblk0p1 rsinit.custom=xyz\n " ;
299+ let custom_value = RefCell :: new ( String :: new ( ) ) ;
300+
301+ let expected = CmdlineOptions {
302+ root : Some ( "/dev/mmcblk0p1" . into ( ) ) ,
303+ ..Default :: default ( )
304+ } ;
305+
306+ let cb = Box :: new ( |key : & str , value : Option < & str > | {
307+ match key {
308+ "rsinit.custom" => {
309+ * custom_value. borrow_mut ( ) = ensure_value ( key, value) ?. to_owned ( ) ;
310+ }
311+ _ => { }
312+ }
313+ Result :: Ok ( ( ) )
314+ } ) ;
315+
316+ let options = CmdlineOptions :: new_with_callbacks ( vec ! [ cb] )
317+ . from_string ( cmdline)
318+ . expect ( "failed" ) ;
245319
246320 assert_eq ! ( options, expected) ;
321+ assert_eq ! ( & * custom_value. borrow( ) , "xyz" ) ;
247322 }
248323}
0 commit comments