11use crate :: {
22 common:: { from_json_file, to_json_file} ,
3- twin:: { Feature , feature:: * } ,
3+ twin:: feature:: * ,
44} ;
55use anyhow:: { Context , Result , bail, ensure} ;
66use azure_iot_sdk:: client:: IotMessage ;
7+ use inotify:: WatchMask ;
78use log:: { info, warn} ;
8- use notify_debouncer_full:: { Debouncer , NoCache , notify:: * } ;
99use serde:: { Deserialize , Serialize } ;
1010use serde_json:: json;
11- use std:: { collections:: HashMap , env, path:: Path } ;
11+ use std:: { collections:: HashMap , env, ffi :: c_int , path:: PathBuf } ;
1212use tokio:: sync:: mpsc:: Sender ;
1313
1414macro_rules! consent_path {
1515 ( ) => {
16- Path :: new ( & env:: var( "CONSENT_DIR_PATH" ) . unwrap_or( "/etc/omnect/consent" . to_string( ) ) )
16+ PathBuf :: from ( & env:: var( "CONSENT_DIR_PATH" ) . unwrap_or( "/etc/omnect/consent" . to_string( ) ) )
1717 } ;
1818}
1919
@@ -53,9 +53,8 @@ pub struct ConsentConfig {
5353 reset_consent_on_fail : bool ,
5454}
5555
56- #[ derive( Default ) ]
5756pub struct DeviceUpdateConsent {
58- file_observer : Option < Debouncer < INotifyWatcher , NoCache > > ,
57+ file_observer : HashMap < c_int , PathBuf > ,
5958 tx_reported_properties : Option < Sender < serde_json:: Value > > ,
6059}
6160
@@ -87,28 +86,23 @@ impl Feature for DeviceUpdateConsent {
8786 . await
8887 }
8988
90- fn command_request_stream ( & mut self ) -> CommandRequestStreamResult {
91- let ( file_observer, stream) = file_modified_stream :: < DeviceUpdateConsent > ( vec ! [
92- request_consent_path!( ) . as_path( ) ,
93- history_consent_path!( ) . as_path( ) ,
94- ] )
95- . context ( "command_request_stream: cannot create file_modified_stream" ) ?;
96- self . file_observer = Some ( file_observer) ;
97- Ok ( Some ( stream) )
98- }
99-
10089 async fn command ( & mut self , cmd : & Command ) -> CommandResult {
10190 match cmd {
102- Command :: FileModified ( file) => {
103- self . report_consent ( from_json_file ( & file. path ) ?) . await ?;
91+ Command :: WatchPath ( cmd) => {
92+ self . report_consent ( from_json_file (
93+ self . file_observer
94+ . get ( & cmd. id )
95+ . context ( "cannot find file for watch id" ) ?,
96+ ) ?)
97+ . await ?;
10498 }
10599 Command :: DesiredGeneralConsent ( cmd) => {
106100 self . update_general_consent ( cmd) . await ?;
107101 }
108102 Command :: UserConsent ( cmd) => {
109103 self . user_consent ( cmd) ?;
110104 }
111- _ => bail ! ( "unexpected command" ) ,
105+ _ => bail ! ( "unexpected command: {cmd:?} " ) ,
112106 } ;
113107
114108 Ok ( None )
@@ -119,6 +113,24 @@ impl DeviceUpdateConsent {
119113 const USER_CONSENT_VERSION : u8 = 1 ;
120114 const ID : & ' static str = "device_update_consent" ;
121115
116+ pub async fn new ( ) -> Result < Self > {
117+ let mut file_observer = HashMap :: new ( ) ;
118+
119+ for path in [ request_consent_path ! ( ) , history_consent_path ! ( ) ] {
120+ file_observer. insert (
121+ add_fs_watch :: < Self > ( & path, WatchMask :: MODIFY )
122+ . await ?
123+ . get_watch_descriptor_id ( ) ,
124+ path,
125+ ) ;
126+ }
127+
128+ Ok ( DeviceUpdateConsent {
129+ tx_reported_properties : None ,
130+ file_observer,
131+ } )
132+ }
133+
122134 fn user_consent ( & self , cmd : & UserConsentCommand ) -> CommandResult {
123135 info ! ( "user consent requested: {cmd:?}" ) ;
124136
@@ -192,27 +204,29 @@ mod tests {
192204 async fn consent_files_changed_test ( ) {
193205 let ( tx_reported_properties, mut rx_reported_properties) = tokio:: sync:: mpsc:: channel ( 100 ) ;
194206 let mut consent = DeviceUpdateConsent {
195- file_observer : None ,
207+ file_observer : HashMap :: from ( [ (
208+ 1 ,
209+ PathBuf :: from ( "testfiles/positive/test_component/user_consent.json" ) ,
210+ ) ] ) ,
196211 tx_reported_properties : Some ( tx_reported_properties) ,
197212 } ;
198213
199214 assert ! (
200215 consent
201- . command( & Command :: FileModified ( PathCommand {
216+ . command( & Command :: WatchPath ( WatchPathCommand {
202217 feature_id: TypeId :: of:: <DeviceUpdateConsent >( ) ,
203- path : Path :: new ( "my-path" ) . to_path_buf ( ) ,
218+ id : 0 ,
204219 } ) )
205220 . await
206221 . unwrap_err( )
207- . chain ( )
208- . any ( |e| e . to_string ( ) . starts_with ( "failed to open for read: " ) )
222+ . to_string ( )
223+ . eq ( "cannot find file for watch id" )
209224 ) ;
210225
211226 consent
212- . command ( & Command :: FileModified ( PathCommand {
227+ . command ( & Command :: WatchPath ( WatchPathCommand {
213228 feature_id : TypeId :: of :: < DeviceUpdateConsent > ( ) ,
214- path : Path :: new ( "testfiles/positive/test_component/user_consent.json" )
215- . to_path_buf ( ) ,
229+ id : 1 ,
216230 } ) )
217231 . await
218232 . unwrap ( ) ;
@@ -227,7 +241,7 @@ mod tests {
227241 async fn desired_consent_test ( ) {
228242 let ( tx_reported_properties, mut rx_reported_properties) = tokio:: sync:: mpsc:: channel ( 100 ) ;
229243 let mut consent = DeviceUpdateConsent {
230- file_observer : None ,
244+ file_observer : HashMap :: new ( ) ,
231245 tx_reported_properties : Some ( tx_reported_properties) ,
232246 } ;
233247
@@ -318,7 +332,7 @@ mod tests {
318332 async fn user_consent_test ( ) {
319333 let ( tx_reported_properties, _rx_reported_properties) = tokio:: sync:: mpsc:: channel ( 100 ) ;
320334 let mut consent = DeviceUpdateConsent {
321- file_observer : None ,
335+ file_observer : HashMap :: new ( ) ,
322336 tx_reported_properties : Some ( tx_reported_properties) ,
323337 } ;
324338
0 commit comments