Skip to main content

systemconfiguration/
dynamic_store.rs

1use std::{
2    ffi::c_void,
3    sync::{Arc, Mutex},
4};
5
6use crate::{
7    bridge::{self, CStringArray},
8    error::Result,
9    ffi,
10    PropertyList, SystemConfigurationError,
11};
12
13struct CallbackState {
14    callback: Box<dyn FnMut(Vec<String>) + Send>,
15}
16
17unsafe extern "C" fn dynamic_store_callback(changed_keys_raw: bridge::RawHandle, info: *mut c_void) {
18    if info.is_null() {
19        return;
20    }
21
22    let mutex = &*info.cast::<Mutex<CallbackState>>();
23    if let Ok(mut state) = mutex.lock() {
24        (state.callback)(bridge::take_string_array(changed_keys_raw));
25    }
26}
27
28#[derive(Clone)]
29pub struct DynamicStore {
30    raw: bridge::OwnedHandle,
31    _callback: Option<Arc<Mutex<CallbackState>>>,
32}
33
34impl std::fmt::Debug for DynamicStore {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        f.debug_struct("DynamicStore").finish_non_exhaustive()
37    }
38}
39
40#[derive(Clone)]
41pub struct DynamicStoreRunLoopSource {
42    raw: bridge::OwnedHandle,
43}
44
45impl std::fmt::Debug for DynamicStoreRunLoopSource {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        f.debug_struct("DynamicStoreRunLoopSource").finish_non_exhaustive()
48    }
49}
50
51impl DynamicStore {
52    pub fn type_id() -> u64 {
53        unsafe { ffi::dynamic_store::sc_dynamic_store_get_type_id() }
54    }
55
56    pub fn new(name: &str) -> Result<Self> {
57        Self::create(name, false, None)
58    }
59
60    pub fn new_with_session_keys(name: &str) -> Result<Self> {
61        Self::create(name, true, None)
62    }
63
64    pub fn new_with_callback<F>(name: &str, callback: F) -> Result<Self>
65    where
66        F: FnMut(Vec<String>) + Send + 'static,
67    {
68        let callback = Arc::new(Mutex::new(CallbackState {
69            callback: Box::new(callback),
70        }));
71        Self::create(name, false, Some(callback))
72    }
73
74    pub fn new_with_session_keys_and_callback<F>(name: &str, callback: F) -> Result<Self>
75    where
76        F: FnMut(Vec<String>) + Send + 'static,
77    {
78        let callback = Arc::new(Mutex::new(CallbackState {
79            callback: Box::new(callback),
80        }));
81        Self::create(name, true, Some(callback))
82    }
83
84    fn create(name: &str, use_session_keys: bool, callback: Option<Arc<Mutex<CallbackState>>>) -> Result<Self> {
85        let name = bridge::cstring(name, "sc_dynamic_store_create")?;
86        let raw = unsafe {
87            callback.as_ref().map_or_else(
88                || ffi::dynamic_store::sc_dynamic_store_create(name.as_ptr(), u8::from(use_session_keys)),
89                |state| {
90                    ffi::dynamic_store::sc_dynamic_store_create_with_callback(
91                        name.as_ptr(),
92                        u8::from(use_session_keys),
93                        Some(dynamic_store_callback),
94                        Arc::as_ptr(state).cast_mut().cast::<c_void>(),
95                    )
96                },
97            )
98        };
99        let raw = bridge::owned_handle_or_last("sc_dynamic_store_create", raw)?;
100        Ok(Self {
101            raw,
102            _callback: callback,
103        })
104    }
105
106    pub fn copy_value(&self, key: &str) -> Result<Option<PropertyList>> {
107        let key = bridge::cstring(key, "sc_dynamic_store_copy_value")?;
108        let raw = unsafe { ffi::dynamic_store::sc_dynamic_store_copy_value(self.raw.as_ptr(), key.as_ptr()) };
109        Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle))
110    }
111
112    pub fn copy_multiple<K, P>(&self, keys: &[K], patterns: &[P]) -> Result<Option<PropertyList>>
113    where
114        K: AsRef<str>,
115        P: AsRef<str>,
116    {
117        let keys = CStringArray::new(keys, "sc_dynamic_store_copy_multiple")?;
118        let patterns = CStringArray::new(patterns, "sc_dynamic_store_copy_multiple")?;
119        let raw = unsafe {
120            ffi::dynamic_store::sc_dynamic_store_copy_multiple(
121                self.raw.as_ptr(),
122                keys.as_ptr(),
123                keys.count(),
124                patterns.as_ptr(),
125                patterns.count(),
126            )
127        };
128        Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle))
129    }
130
131    pub fn add_value(&self, key: &str, value: &PropertyList) -> Result<()> {
132        let key = bridge::cstring(key, "sc_dynamic_store_add_value")?;
133        let ok = unsafe {
134            ffi::dynamic_store::sc_dynamic_store_add_value(self.raw.as_ptr(), key.as_ptr(), value.as_ptr())
135        };
136        bridge::bool_result("sc_dynamic_store_add_value", ok)
137    }
138
139    pub fn add_temporary_value(&self, key: &str, value: &PropertyList) -> Result<()> {
140        let key = bridge::cstring(key, "sc_dynamic_store_add_temporary_value")?;
141        let ok = unsafe {
142            ffi::dynamic_store::sc_dynamic_store_add_temporary_value(
143                self.raw.as_ptr(),
144                key.as_ptr(),
145                value.as_ptr(),
146            )
147        };
148        bridge::bool_result("sc_dynamic_store_add_temporary_value", ok)
149    }
150
151    pub fn set_value(&self, key: &str, value: &PropertyList) -> Result<()> {
152        let key = bridge::cstring(key, "sc_dynamic_store_set_value")?;
153        let ok = unsafe {
154            ffi::dynamic_store::sc_dynamic_store_set_value(self.raw.as_ptr(), key.as_ptr(), value.as_ptr())
155        };
156        bridge::bool_result("sc_dynamic_store_set_value", ok)
157    }
158
159    pub fn set_multiple<R, N>(
160        &self,
161        keys_to_set: Option<&PropertyList>,
162        keys_to_remove: &[R],
163        keys_to_notify: &[N],
164    ) -> Result<()>
165    where
166        R: AsRef<str>,
167        N: AsRef<str>,
168    {
169        let keys_to_remove = CStringArray::new(keys_to_remove, "sc_dynamic_store_set_multiple")?;
170        let keys_to_notify = CStringArray::new(keys_to_notify, "sc_dynamic_store_set_multiple")?;
171        let ok = unsafe {
172            ffi::dynamic_store::sc_dynamic_store_set_multiple(
173                self.raw.as_ptr(),
174                keys_to_set.map_or(std::ptr::null_mut(), PropertyList::as_ptr),
175                keys_to_remove.as_ptr(),
176                keys_to_remove.count(),
177                keys_to_notify.as_ptr(),
178                keys_to_notify.count(),
179            )
180        };
181        bridge::bool_result("sc_dynamic_store_set_multiple", ok)
182    }
183
184    pub fn remove_value(&self, key: &str) -> Result<()> {
185        let key = bridge::cstring(key, "sc_dynamic_store_remove_value")?;
186        let ok = unsafe { ffi::dynamic_store::sc_dynamic_store_remove_value(self.raw.as_ptr(), key.as_ptr()) };
187        bridge::bool_result("sc_dynamic_store_remove_value", ok)
188    }
189
190    pub fn notify_value(&self, key: &str) -> Result<()> {
191        let key = bridge::cstring(key, "sc_dynamic_store_notify_value")?;
192        let ok = unsafe { ffi::dynamic_store::sc_dynamic_store_notify_value(self.raw.as_ptr(), key.as_ptr()) };
193        bridge::bool_result("sc_dynamic_store_notify_value", ok)
194    }
195
196    pub fn copy_key_list(&self, pattern: &str) -> Result<Vec<String>> {
197        let pattern = bridge::cstring(pattern, "sc_dynamic_store_copy_key_list")?;
198        let raw = unsafe { ffi::dynamic_store::sc_dynamic_store_copy_key_list(self.raw.as_ptr(), pattern.as_ptr()) };
199        Ok(bridge::take_string_array(raw))
200    }
201
202    pub fn set_notification_keys<K, P>(&self, keys: &[K], patterns: &[P]) -> Result<()>
203    where
204        K: AsRef<str>,
205        P: AsRef<str>,
206    {
207        let keys = CStringArray::new(keys, "sc_dynamic_store_set_notification_keys")?;
208        let patterns = CStringArray::new(patterns, "sc_dynamic_store_set_notification_keys")?;
209        let ok = unsafe {
210            ffi::dynamic_store::sc_dynamic_store_set_notification_keys(
211                self.raw.as_ptr(),
212                keys.as_ptr(),
213                keys.count(),
214                patterns.as_ptr(),
215                patterns.count(),
216            )
217        };
218        bridge::bool_result("sc_dynamic_store_set_notification_keys", ok)
219    }
220
221    pub fn create_run_loop_source(&self, order: isize) -> Result<DynamicStoreRunLoopSource> {
222        let raw = unsafe { ffi::dynamic_store::sc_dynamic_store_create_run_loop_source(self.raw.as_ptr(), order) };
223        let raw = bridge::owned_handle_or_last("sc_dynamic_store_create_run_loop_source", raw)?;
224        Ok(DynamicStoreRunLoopSource { raw })
225    }
226
227    pub fn set_dispatch_queue_global(&self) -> Result<()> {
228        let ok = unsafe { ffi::dynamic_store::sc_dynamic_store_set_dispatch_queue_global(self.raw.as_ptr()) };
229        bridge::bool_result("sc_dynamic_store_set_dispatch_queue_global", ok)
230    }
231
232    pub fn clear_dispatch_queue(&self) -> Result<()> {
233        let ok = unsafe { ffi::dynamic_store::sc_dynamic_store_clear_dispatch_queue(self.raw.as_ptr()) };
234        bridge::bool_result("sc_dynamic_store_clear_dispatch_queue", ok)
235    }
236
237    pub fn copy_notified_keys(&self) -> Vec<String> {
238        let raw = unsafe { ffi::dynamic_store::sc_dynamic_store_copy_notified_keys(self.raw.as_ptr()) };
239        bridge::take_string_array(raw)
240    }
241
242    pub fn computer_name(&self) -> Option<String> {
243        bridge::take_optional_string(unsafe {
244            ffi::dynamic_store::sc_dynamic_store_copy_computer_name(self.raw.as_ptr())
245        })
246    }
247
248    pub fn local_host_name(&self) -> Option<String> {
249        bridge::take_optional_string(unsafe {
250            ffi::dynamic_store::sc_dynamic_store_copy_local_host_name(self.raw.as_ptr())
251        })
252    }
253
254    pub fn location(&self) -> Option<String> {
255        bridge::take_optional_string(unsafe {
256            ffi::dynamic_store::sc_dynamic_store_copy_location(self.raw.as_ptr())
257        })
258    }
259
260    pub fn proxies(&self) -> Option<PropertyList> {
261        let raw = unsafe { ffi::dynamic_store::sc_dynamic_store_copy_proxies(self.raw.as_ptr()) };
262        unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle)
263    }
264
265    pub fn dhcp_info(&self, service_id: Option<&str>) -> Result<Option<PropertyList>> {
266        let service_id = bridge::optional_cstring(service_id, "sc_dynamic_store_copy_dhcp_info")?;
267        let raw = unsafe {
268            ffi::dynamic_store::sc_dynamic_store_copy_dhcp_info(
269                self.raw.as_ptr(),
270                service_id.as_ref().map_or(std::ptr::null(), |value| value.as_ptr()),
271            )
272        };
273        Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle))
274    }
275
276    pub fn dhcp_option_data(info: &PropertyList, code: u8) -> Option<PropertyList> {
277        unsafe { bridge::OwnedHandle::from_raw(ffi::dynamic_store::sc_dhcp_info_copy_option_data(info.as_ptr(), code)) }
278            .map(PropertyList::from_owned_handle)
279    }
280
281    pub fn dhcp_lease_start_time(info: &PropertyList) -> Option<PropertyList> {
282        unsafe { bridge::OwnedHandle::from_raw(ffi::dynamic_store::sc_dhcp_info_copy_lease_start_time(info.as_ptr())) }
283            .map(PropertyList::from_owned_handle)
284    }
285
286    pub fn dhcp_lease_expiration_time(info: &PropertyList) -> Option<PropertyList> {
287        unsafe {
288            bridge::OwnedHandle::from_raw(ffi::dynamic_store::sc_dhcp_info_copy_lease_expiration_time(
289                info.as_ptr(),
290            ))
291        }
292        .map(PropertyList::from_owned_handle)
293    }
294
295    pub fn key_create<A: AsRef<str>>(format: &str, arguments: &[A]) -> Result<String> {
296        let format = bridge::cstring(format, "sc_dynamic_store_key_create")?;
297        let arguments = CStringArray::new(arguments, "sc_dynamic_store_key_create")?;
298        bridge::take_optional_string(unsafe {
299            ffi::dynamic_store::sc_dynamic_store_key_create(format.as_ptr(), arguments.as_ptr(), arguments.count())
300        })
301        .ok_or_else(|| {
302            SystemConfigurationError::null(
303                "sc_dynamic_store_key_create",
304                "bridge returned null dynamic-store formatted key",
305            )
306        })
307    }
308
309    pub fn network_global_entity_key(domain: &str, entity: &str) -> Result<String> {
310        let domain = bridge::cstring(domain, "sc_dynamic_store_key_create_network_global_entity")?;
311        let entity = bridge::cstring(entity, "sc_dynamic_store_key_create_network_global_entity")?;
312        bridge::take_optional_string(unsafe {
313            ffi::dynamic_store::sc_dynamic_store_key_create_network_global_entity(
314                domain.as_ptr(),
315                entity.as_ptr(),
316            )
317        })
318        .ok_or_else(|| {
319            SystemConfigurationError::null(
320                "sc_dynamic_store_key_create_network_global_entity",
321                "bridge returned null dynamic-store global entity key",
322            )
323        })
324    }
325
326    pub fn network_interface_key(domain: &str) -> Result<String> {
327        let domain = bridge::cstring(domain, "sc_dynamic_store_key_create_network_interface")?;
328        bridge::take_optional_string(unsafe {
329            ffi::dynamic_store::sc_dynamic_store_key_create_network_interface(domain.as_ptr())
330        })
331        .ok_or_else(|| {
332            SystemConfigurationError::null(
333                "sc_dynamic_store_key_create_network_interface",
334                "bridge returned null dynamic-store interface key",
335            )
336        })
337    }
338
339    pub fn network_interface_entity_key(
340        domain: &str,
341        interface_name: &str,
342        entity: Option<&str>,
343    ) -> Result<String> {
344        let domain = bridge::cstring(domain, "sc_dynamic_store_key_create_network_interface_entity")?;
345        let interface_name = bridge::cstring(
346            interface_name,
347            "sc_dynamic_store_key_create_network_interface_entity",
348        )?;
349        let entity = bridge::optional_cstring(entity, "sc_dynamic_store_key_create_network_interface_entity")?;
350        bridge::take_optional_string(unsafe {
351            ffi::dynamic_store::sc_dynamic_store_key_create_network_interface_entity(
352                domain.as_ptr(),
353                interface_name.as_ptr(),
354                entity.as_ref().map_or(std::ptr::null(), |value| value.as_ptr()),
355            )
356        })
357        .ok_or_else(|| {
358            SystemConfigurationError::null(
359                "sc_dynamic_store_key_create_network_interface_entity",
360                "bridge returned null dynamic-store interface-entity key",
361            )
362        })
363    }
364
365    pub fn network_service_entity_key(
366        domain: &str,
367        service_id: &str,
368        entity: Option<&str>,
369    ) -> Result<String> {
370        let domain = bridge::cstring(domain, "sc_dynamic_store_key_create_network_service_entity")?;
371        let service_id = bridge::cstring(service_id, "sc_dynamic_store_key_create_network_service_entity")?;
372        let entity = bridge::optional_cstring(entity, "sc_dynamic_store_key_create_network_service_entity")?;
373        bridge::take_optional_string(unsafe {
374            ffi::dynamic_store::sc_dynamic_store_key_create_network_service_entity(
375                domain.as_ptr(),
376                service_id.as_ptr(),
377                entity.as_ref().map_or(std::ptr::null(), |value| value.as_ptr()),
378            )
379        })
380        .ok_or_else(|| {
381            SystemConfigurationError::null(
382                "sc_dynamic_store_key_create_network_service_entity",
383                "bridge returned null dynamic-store service-entity key",
384            )
385        })
386    }
387
388    pub fn computer_name_key() -> Result<String> {
389        bridge::take_optional_string(unsafe { ffi::dynamic_store::sc_dynamic_store_key_create_computer_name() })
390            .ok_or_else(|| {
391                SystemConfigurationError::null(
392                    "sc_dynamic_store_key_create_computer_name",
393                    "bridge returned null computer-name notification key",
394                )
395            })
396    }
397
398    pub fn console_user_key() -> Result<String> {
399        bridge::take_optional_string(unsafe { ffi::dynamic_store::sc_dynamic_store_key_create_console_user() })
400            .ok_or_else(|| {
401                SystemConfigurationError::null(
402                    "sc_dynamic_store_key_create_console_user",
403                    "bridge returned null console-user notification key",
404                )
405            })
406    }
407
408    pub fn host_names_key() -> Result<String> {
409        bridge::take_optional_string(unsafe { ffi::dynamic_store::sc_dynamic_store_key_create_host_names() })
410            .ok_or_else(|| {
411                SystemConfigurationError::null(
412                    "sc_dynamic_store_key_create_host_names",
413                    "bridge returned null host-names notification key",
414                )
415            })
416    }
417
418    pub fn location_key() -> Result<String> {
419        bridge::take_optional_string(unsafe { ffi::dynamic_store::sc_dynamic_store_key_create_location() })
420            .ok_or_else(|| {
421                SystemConfigurationError::null(
422                    "sc_dynamic_store_key_create_location",
423                    "bridge returned null location notification key",
424                )
425            })
426    }
427
428    pub fn proxies_key() -> Result<String> {
429        bridge::take_optional_string(unsafe { ffi::dynamic_store::sc_dynamic_store_key_create_proxies() })
430            .ok_or_else(|| {
431                SystemConfigurationError::null(
432                    "sc_dynamic_store_key_create_proxies",
433                    "bridge returned null proxies notification key",
434                )
435            })
436    }
437
438}
439
440impl DynamicStoreRunLoopSource {
441    pub fn schedule_current_default_mode(&self) -> Result<()> {
442        let ok = unsafe { ffi::dynamic_store::sc_run_loop_source_schedule_current_default_mode(self.raw.as_ptr()) };
443        bridge::bool_result("sc_run_loop_source_schedule_current_default_mode", ok)
444    }
445
446    pub fn unschedule_current_default_mode(&self) -> Result<()> {
447        let ok = unsafe {
448            ffi::dynamic_store::sc_run_loop_source_unschedule_current_default_mode(self.raw.as_ptr())
449        };
450        bridge::bool_result("sc_run_loop_source_unschedule_current_default_mode", ok)
451    }
452}