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