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