@@ -12,39 +12,163 @@ use std::io::{Read, Seek, SeekFrom};
1212use std:: os:: unix:: fs:: MetadataExt ;
1313use std:: time:: { Duration , Instant } ;
1414
15+ use argh;
16+
17+ /// PLDM file host
18+ #[ derive( argh:: FromArgs ) ]
19+ struct Args {
20+ /// file to serve
21+ #[ argh(
22+ option,
23+ short = 'f' ,
24+ default = r#""pldm-file-host.bin".to_string()"#
25+ ) ]
26+ file : String ,
27+
28+ /// serve zeroes
29+ #[ argh( switch) ]
30+ zeroes : bool ,
31+
32+ /// serve a file containing the offset at intervals
33+ #[ argh( switch) ]
34+ offset : bool ,
35+ }
36+
1537struct Host {
16- file : RefCell < File > ,
17- stamp : RefCell < ( Instant , usize ) > ,
38+ file_size : u32 ,
39+ speed : Speed ,
40+ mode : Mode ,
41+ }
42+
43+ enum Mode {
44+ File {
45+ file : RefCell < File > ,
46+ filename : String ,
47+ } ,
48+ Offset ,
49+ Zeroes ,
50+ }
51+
52+ impl Mode {
53+ fn filename ( & self ) -> String {
54+ match self {
55+ Self :: File { filename, .. } => filename. clone ( ) ,
56+ Self :: Offset => "offset" . to_string ( ) ,
57+ Self :: Zeroes => "zeroes" . to_string ( ) ,
58+ }
59+ }
1860}
1961
20- const FILENAME : & str = "pldm-file-host.bin" ;
2162// Arbitrary, 0 is reserved.
2263const PDR_HANDLE : u32 = 1 ;
2364
2465impl Host {
25- fn new ( ) -> Result < Self > {
66+ fn new ( args : & Args ) -> Result < Self > {
67+ let speed = Speed :: new ( ) ;
68+
69+ let mut file_size = u32:: MAX ;
70+ let mode = if args. zeroes {
71+ info ! ( "Serving zeroes" ) ;
72+ Mode :: Zeroes
73+ } else if args. offset {
74+ info ! ( "Serving offset" ) ;
75+ Mode :: Offset
76+ } else {
77+ let file = File :: open ( & args. file ) . with_context ( || {
78+ format ! ( "cannot open input file {}" , args. file)
79+ } ) ?;
80+ file_size = file
81+ . metadata ( )
82+ . context ( "Metadata failed" ) ?
83+ . size ( )
84+ . try_into ( )
85+ . context ( "File size > u32" ) ?;
86+ info ! ( "Serving {}, {} bytes" , args. file, file_size) ;
87+ Mode :: File {
88+ file : RefCell :: new ( file) ,
89+ filename : args. file . clone ( ) ,
90+ }
91+ } ;
92+
2693 Ok ( Self {
27- file : RefCell :: new ( File :: open ( FILENAME ) . with_context ( || {
28- format ! ( "cannot open input file {FILENAME}" )
29- } ) ?) ,
30- stamp : RefCell :: new ( ( Instant :: now ( ) , 0 ) ) ,
94+ mode,
95+ file_size,
96+ speed,
3197 } )
3298 }
3399}
34100
35101impl pldm_file:: host:: Host for Host {
36102 fn read ( & self , buf : & mut [ u8 ] , offset : usize ) -> std:: io:: Result < usize > {
103+ self . speed . update ( offset) ;
104+
105+ match & self . mode {
106+ Mode :: File { file, .. } => {
107+ let mut file = file. borrow_mut ( ) ;
108+ file. seek ( SeekFrom :: Start ( offset as u64 ) ) ?;
109+ read_whole ( & mut file, buf)
110+ }
111+ Mode :: Zeroes => {
112+ buf. fill ( 0 ) ;
113+ Ok ( buf. len ( ) )
114+ }
115+ Mode :: Offset => {
116+ let mut o = offset;
117+ for b in buf. chunks_mut ( 64 ) {
118+ if let Some ( b) = b. get_mut ( ..4 ) {
119+ b. copy_from_slice ( & o. to_be_bytes ( ) )
120+ }
121+ o += b. len ( ) ;
122+ }
123+ Ok ( buf. len ( ) )
124+ }
125+ }
126+ }
127+ }
128+
129+ /// Reads into buf until EOF or error.
130+ ///
131+ /// A returned `length < buf.len()` means EOF. `buf` should not be empty.
132+ fn read_whole ( f : & mut File , buf : & mut [ u8 ] ) -> std:: io:: Result < usize > {
133+ let total = buf. len ( ) ;
134+ let mut rest = buf;
135+ while !rest. is_empty ( ) {
136+ match f. read ( rest) {
137+ // EOF
138+ Ok ( 0 ) => return Ok ( total - rest. len ( ) ) ,
139+ Ok ( l) => rest = & mut rest[ l..] ,
140+ Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: Interrupted => continue ,
141+ Err ( e) => return Err ( e) ,
142+ }
143+ }
144+ // Full output
145+ return Ok ( total) ;
146+ }
147+
148+ struct Speed {
149+ stamp : RefCell < ( Instant , usize ) > ,
150+ }
151+
152+ impl Speed {
153+ fn new ( ) -> Self {
154+ Self {
155+ stamp : RefCell :: new ( ( Instant :: now ( ) , 0 ) ) ,
156+ }
157+ }
158+
159+ fn update ( & self , offset : usize ) {
37160 let mut stamp = self . stamp . borrow_mut ( ) ;
38161 let now = Instant :: now ( ) ;
162+ if offset == 0 {
163+ stamp. 0 = now;
164+ }
165+
39166 let del = now - stamp. 0 ;
40167 if del > Duration :: from_secs ( 2 ) {
41168 let rate = ( offset - stamp. 1 ) / del. as_millis ( ) as usize ;
42169 info ! ( "{rate} kB/s, offset {offset}" ) ;
43170 * stamp = ( now, offset) ;
44171 }
45- let mut file = self . file . borrow_mut ( ) ;
46- file. seek ( SeekFrom :: Start ( offset as u64 ) ) ?;
47- file. read ( buf)
48172 }
49173}
50174
@@ -57,7 +181,10 @@ fn main() -> Result<()> {
57181 let mut listener = MctpLinuxAsyncListener :: new ( mctp:: MCTP_TYPE_PLDM , None ) ?;
58182 let mut pldm_ctrl = pldm:: control:: responder:: Responder :: < 2 > :: new ( ) ;
59183 let mut pldm_file = FileResponder :: new ( ) ;
60- let mut host = Host :: new ( ) . context ( "unable to create file host" ) ?;
184+
185+ let args: Args = argh:: from_env ( ) ;
186+
187+ let mut host = Host :: new ( & args) . context ( "unable to create file host" ) ?;
61188
62189 FileResponder :: register ( & mut pldm_ctrl) ?;
63190
@@ -180,15 +307,6 @@ fn handle_get_pdr(
180307 return Ok ( plat_codes:: INVALID_RECORD_CHANGE_NUMBER ) ;
181308 }
182309
183- let file_max_size = host
184- . file
185- . borrow ( )
186- . metadata ( )
187- . context ( "Metadata failed" ) ?
188- . size ( )
189- . try_into ( )
190- . context ( "File size > u32" ) ?;
191-
192310 let pdr_resp = GetPDRResp :: new_single (
193311 PDR_HANDLE ,
194312 PdrRecord :: FileDescriptor ( FileDescriptorPdr {
@@ -204,10 +322,15 @@ fn handle_get_pdr(
204322 oem_file_classification : 0 ,
205323 capabilities : file_capabilities:: EX_READ_OPEN ,
206324 file_version : 0xFFFFFFFF ,
207- file_max_size,
325+ file_max_size : host . file_size ,
208326 // TODO
209327 file_max_desc_count : 1 ,
210- file_name : FILENAME . try_into ( ) . expect ( "Filename too long" ) ,
328+ file_name : host
329+ . mode
330+ . filename ( )
331+ . as_str ( )
332+ . try_into ( )
333+ . expect ( "Filename too long" ) ,
211334 oem_file_classification_name : Default :: default ( ) ,
212335 } ) ,
213336 ) ?;
0 commit comments