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