cdns_rs/sync/
caches.rs

1/*-
2 * cdns-rs - a simple sync/async DNS query library
3 * 
4 * Copyright (C) 2020  Aleksandr Morozov
5 * 
6 * Copyright (C) 2025 Aleksandr Morozov
7 * 
8 * The syslog-rs crate can be redistributed and/or modified
9 * under the terms of either of the following licenses:
10 *
11 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
12 *                     
13 *   2. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
14 */
15
16/// This file contains configuration caches.
17
18use std::path::Path;
19use std::sync::{Arc, LazyLock, Mutex};
20use std::time::SystemTime;
21use std::fs;
22
23
24use crate::cfg_host_parser::HostConfig;
25use crate::cfg_resolv_parser::ResolveConfig;
26use crate::{error::*, internal_error, internal_error_map, write_error};
27
28use super::cfg_parsers::*;
29
30pub static CACHE: LazyLock<CachesController> = LazyLock::new(|| { CachesController::new() });
31
32pub trait CacheOperations
33{
34    fn is_reload_allowed(&self) -> bool;
35}
36
37#[derive(Clone, Debug)]
38struct CacheTime<'cache>
39{
40    path: &'cache Path,
41    last_modif: SystemTime,
42}
43
44impl<'cache> CacheTime<'cache>
45{
46    fn new(path: &'cache Path) -> CDnsResult<Self>
47    {
48        return Ok(Self { path: path, last_modif: Self::get_last_modified(path)? });
49    }
50
51    fn fake(path: &'cache Path) -> Self
52    {
53        return Self { path: path, last_modif: SystemTime::now() };
54    }
55
56    fn get_last_modified(path: &'cache Path) -> CDnsResult<SystemTime>
57    {
58        let metadata = fs::metadata(path).map_err(|e| 
59            internal_error_map!(CDnsErrorType::InternalError, "fs::metadata::modified() not supported on this platform: '{}'", e)
60        )?;
61
62        let last_modif = 
63            metadata.modified().map_err(|e| 
64                internal_error_map!(CDnsErrorType::InternalError, "fs::metadata::modified() not supported on this platform: '{}'", e)
65            )?;
66
67        return Ok(last_modif);
68    }
69
70    fn check_modified(&mut self) -> CDnsResult<bool>
71    {
72        // in case if get_last_modified() will return Err, return what was cached prevously
73        let last_modif = 
74            match Self::get_last_modified(self.path)
75            {
76                Ok(t) => t,
77                Err(e) => internal_error!(CDnsErrorType::InternalError, "{}", e),
78            };
79
80        return Ok(self.last_modif != last_modif);
81        /*{
82            // reload
83            match ResolveConfEntry::parse_resolve_cfg()
84            {
85                Ok(r) => 
86                    self.cache = Arc::new(r),
87                Err(e) =>
88                    write_error!("{}", e),
89            }
90             
91        }
92        
93        return;*/
94    }
95}
96
97#[derive(Clone, Debug)]
98pub struct CacheInstance<T: Default + ConfigParser<T> + CacheOperations>
99{
100    last_modif: CacheTime<'static>,
101    cache: Arc<T>,
102}
103
104impl<T: Default + ConfigParser<T> + CacheOperations> Default for CacheInstance<T>
105{
106    fn default() -> Self 
107    {
108        return 
109            Self 
110            {
111                last_modif: CacheTime::fake(T::get_file_path()), 
112                cache: Arc::new(T::default()),
113              //  f: PhantomData,
114            };
115    }
116}
117
118impl<T: Default + ConfigParser<T> + CacheOperations> CacheInstance<T>
119{
120    fn new() -> CDnsResult<Self>
121    {
122        return Ok(
123            Self 
124            {
125                last_modif: CacheTime::new(T::get_file_path())?, 
126                cache: Arc::new(T::parse_config()?),
127                //f: PhantomData,
128            }
129        );
130    }
131
132    fn read_config() -> CDnsResult<Arc<T>>
133    {
134        return Ok(Arc::new(T::parse_config()?));
135    }
136
137    fn check_modified(&mut self) -> CDnsResult<()>
138    {
139        // in case if get_last_modified() will return Err, save what was cached prevously
140        if self.cache.is_reload_allowed() == false
141        {
142            // just return OK
143            return Ok(());
144        }
145
146        if self.last_modif.check_modified()? == true
147        {
148            // reload
149            self.cache = Self::read_config()?;
150        }
151        
152        return Ok(());
153    }
154
155    fn clone_cache(&self) -> Arc<T>
156    {
157        return self.cache.clone();
158    }
159}
160
161pub struct CachesController
162{
163    resolv_cache: Mutex<CacheInstance<ResolveConfig>>,
164    host_cache: Mutex<CacheInstance<HostConfig>>
165}
166
167unsafe impl Sync for CachesController{}
168unsafe impl Send for CachesController{}
169
170impl CachesController
171{
172    /// Spawns instance and reads the configuration.
173    fn new() -> Self
174    {
175        let resolve_cache_res = CacheInstance::<ResolveConfig>::new();
176
177        let host_cache_res = CacheInstance::<HostConfig>::new();
178
179        let resolve_cache = 
180            match resolve_cache_res
181            {
182                Ok(r) => r,
183                Err(e) =>
184                {
185                    write_error!(e);
186
187                    CacheInstance::default()
188                }
189            };
190        
191        let host_cache = 
192            match host_cache_res
193            {
194                Ok(r) => r,
195                Err(e) =>
196                {
197                    write_error!(e);
198
199                    CacheInstance::default()
200                }
201            };
202    
203
204        return 
205            CachesController
206            { 
207                resolv_cache: Mutex::new(resolve_cache),
208                host_cache: Mutex::new(host_cache),
209            };
210    }
211
212    pub 
213    fn is_resolve_cached(&self) -> bool
214    {
215        return !self.resolv_cache.lock().unwrap().cache.is_default();
216    }
217
218    pub 
219    fn is_host_cached(&self) -> bool
220    {
221        return !self.host_cache.lock().unwrap().cache.is_default();
222    }
223
224    fn try_cache_resolve(&self) -> CDnsResult<Arc<ResolveConfig>>
225    {
226        let mut mutx_resolv = self.resolv_cache.lock().unwrap();
227        mutx_resolv.check_modified()?;
228
229        return Ok(mutx_resolv.clone_cache());
230    }
231
232    /// Clones the [ResolveConfig] [Arc] (atomic reference counter) reference.
233    pub 
234    fn clone_resolve_list(&self) -> CDnsResult<Arc<ResolveConfig>>
235    {
236        // clone from cache and/or reload cache
237        match self.try_cache_resolve()
238        {
239            Ok(r) => 
240                return Ok(r),
241            Err(e) => 
242            {
243                write_error!(e);
244
245                // try to read directly, this ignores all reload restrictions
246                return CacheInstance::<ResolveConfig>::read_config();
247            }
248        }
249    }
250
251    fn try_cache_host(&self) -> CDnsResult<Arc<HostConfig>>
252    {
253        let mut mutx_host = self.host_cache.lock().unwrap();
254        mutx_host.check_modified()?;
255
256        return Ok(mutx_host.clone_cache());
257    }
258
259    /// Clones the [HostConfig] [Arc] (atomic reference counter) reference.
260    pub 
261    fn clone_host_list(&self) -> CDnsResult<Arc<HostConfig>>
262    {
263        match self.try_cache_host()
264        {
265            Ok(r) => 
266                return Ok(r),
267            Err(e) =>
268            {
269                write_error!(e);
270
271                // try to read directly, this ignores all reload restrictions
272                return CacheInstance::<HostConfig>::read_config();
273            }
274        }
275    }
276}
277
278
279
280#[cfg(test)]
281mod tests
282{
283    use crate::sync::caches::CACHE;
284
285    #[test]
286    fn test_init()
287    {
288        let res = CACHE.clone_resolve_list();
289        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
290
291        let res = res.unwrap();
292
293        println!("{:?}", res);
294
295        let res = CACHE.clone_host_list();
296        assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
297
298        let res = res.unwrap();
299
300        println!("{:?}", res);
301    }
302}