6
6
// option. This file may not be copied, modified, or distributed
7
7
// except according to those terms.
8
8
9
+ use core_foundation:: array:: { CFArray , CFArrayRef } ;
9
10
use core_foundation:: base:: TCFType ;
10
11
use core_foundation:: boolean:: CFBoolean ;
11
12
use core_foundation:: dictionary:: CFDictionary ;
12
13
use core_foundation:: propertylist:: { CFPropertyList , CFPropertyListSubClass } ;
14
+ use core_foundation:: runloop:: CFRunLoopSource ;
13
15
use core_foundation:: string:: CFString ;
14
16
use core_foundation_sys:: base:: { CFRelease , kCFAllocatorDefault} ;
15
17
16
- use system_configuration_sys:: dynamic_store:: * ;
18
+ pub use system_configuration_sys:: dynamic_store:: * ;
17
19
20
+ use std:: os:: raw:: c_void;
18
21
use std:: ptr;
19
22
23
+ /// Struct describing the callback happening when a watched value in the dynamic store is changed.
24
+ pub struct SCDynamicStoreCallBackContext < T > {
25
+ /// The callback function that will be called when a watched value in the dynamic store is
26
+ /// changed.
27
+ pub callout : SCDynamicStoreCallBackT < T > ,
28
+
29
+ /// The argument passed to each `callout` call. Can be used to keep state between
30
+ /// callbacks.
31
+ pub info : T ,
32
+ }
33
+
34
+ /// Signature for callback functions getting called when a watched value in the dynamic store is
35
+ /// changed.
36
+ ///
37
+ /// This is the safe callback definition, abstracting over the lower level `SCDynamicStoreCallBack`
38
+ /// from the `system-configuration-sys` crate.
39
+ pub type SCDynamicStoreCallBackT < T > = fn (
40
+ store : SCDynamicStore ,
41
+ changed_keys : CFArray < CFString > ,
42
+ info : & mut T ,
43
+ ) ;
44
+
45
+ /// Builder for [`SCDynamicStore`] sessions.
46
+ ///
47
+ /// [`SCDynamicStore`]: struct.SCDynamicStore.html
48
+ pub struct SCDynamicStoreBuilder < T > {
49
+ name : CFString ,
50
+ session_keys : bool ,
51
+ callback_context : Option < SCDynamicStoreCallBackContext < T > > ,
52
+ }
53
+
54
+ impl SCDynamicStoreBuilder < ( ) > {
55
+ /// Creates a new builder. `name` is used as the name parameter when creating the
56
+ /// [`SCDynamicStore`] session.
57
+ ///
58
+ /// [`SCDynamicStore`]: struct.SCDynamicStore.html
59
+ pub fn new < S : Into < CFString > > ( name : S ) -> Self {
60
+ SCDynamicStoreBuilder {
61
+ name : name. into ( ) ,
62
+ session_keys : false ,
63
+ callback_context : None ,
64
+ }
65
+ }
66
+ }
67
+
68
+ impl < T > SCDynamicStoreBuilder < T > {
69
+ /// Set wether or not the created [`SCDynamicStore`] should have session keys or not.
70
+ /// See [`SCDynamicStoreCreateWithOptions`] for details.
71
+ ///
72
+ /// Defaults to `false`.
73
+ ///
74
+ /// [`SCDynamicStore`]: struct.SCDynamicStore.html
75
+ /// [`SCDynamicStoreCreateWithOptions`]: https://developer.apple.com/documentation/systemconfiguration/1437818-scdynamicstorecreatewithoptions?language=objc
76
+ pub fn session_keys ( mut self , session_keys : bool ) -> Self {
77
+ self . session_keys = session_keys;
78
+ self
79
+ }
80
+
81
+ /// Set a callback context (callback function and data to pass to each callback call).
82
+ ///
83
+ /// Defaults to having callbacks disabled.
84
+ pub fn callback_context < T2 > (
85
+ self ,
86
+ callback_context : SCDynamicStoreCallBackContext < T2 > ,
87
+ ) -> SCDynamicStoreBuilder < T2 > {
88
+ SCDynamicStoreBuilder {
89
+ name : self . name ,
90
+ session_keys : self . session_keys ,
91
+ callback_context : Some ( callback_context) ,
92
+ }
93
+ }
94
+
95
+ /// Create the dynamic store session.
96
+ pub fn build ( mut self ) -> SCDynamicStore {
97
+ let store_options = self . create_store_options ( ) ;
98
+ if let Some ( callback_context) = self . callback_context . take ( ) {
99
+ SCDynamicStore :: create (
100
+ & self . name ,
101
+ & store_options,
102
+ Some ( convert_callback :: < T > ) ,
103
+ & mut self . create_context ( callback_context) ,
104
+ )
105
+ } else {
106
+ SCDynamicStore :: create ( & self . name , & store_options, None , ptr:: null_mut ( ) )
107
+ }
108
+ }
109
+
110
+ fn create_store_options ( & self ) -> CFDictionary {
111
+ let key = unsafe { CFString :: wrap_under_create_rule ( kSCDynamicStoreUseSessionKeys) } ;
112
+ let value = CFBoolean :: from ( self . session_keys ) ;
113
+ CFDictionary :: from_CFType_pairs ( & [ ( key, value) ] )
114
+ }
115
+
116
+ fn create_context (
117
+ & self ,
118
+ callback_context : SCDynamicStoreCallBackContext < T > ,
119
+ ) -> SCDynamicStoreContext {
120
+ // move the callback context struct to the heap and "forget" it.
121
+ // It will later be brought back into the Rust typesystem and freed in
122
+ // `release_callback_context`
123
+ let info_ptr = Box :: into_raw ( Box :: new ( callback_context) ) ;
124
+
125
+ SCDynamicStoreContext {
126
+ version : 0 ,
127
+ info : info_ptr as * mut _ as * mut c_void ,
128
+ retain : None ,
129
+ release : Some ( release_callback_context :: < T > ) ,
130
+ copyDescription : None ,
131
+ }
132
+ }
133
+ }
134
+
20
135
/// Access to the key-value pairs in the dynamic store of a running system.
136
+ ///
137
+ /// Use the [`SCDynamicStoreBuilder`] to create instances of this.
138
+ ///
139
+ /// [`SCDynamicStoreBuilder`]: struct.SCDynamicStoreBuilder.html
21
140
pub struct SCDynamicStore ( SCDynamicStoreRef ) ;
22
141
23
142
impl Drop for SCDynamicStore {
@@ -31,39 +150,19 @@ impl_TCFType!(SCDynamicStore, SCDynamicStoreRef, SCDynamicStoreGetTypeID);
31
150
impl SCDynamicStore {
32
151
/// Creates a new session used to interact with the dynamic store maintained by the System
33
152
/// Configuration server.
34
- pub fn create < S : Into < CFString > > ( name : S ) -> Self {
35
- let cf_name = name. into ( ) ;
153
+ fn create (
154
+ name : & CFString ,
155
+ store_options : & CFDictionary ,
156
+ callout : SCDynamicStoreCallBack ,
157
+ context : * mut SCDynamicStoreContext ,
158
+ ) -> Self {
36
159
unsafe {
37
- let store = SCDynamicStoreCreate (
38
- kCFAllocatorDefault,
39
- cf_name. as_concrete_TypeRef ( ) ,
40
- None ,
41
- ptr:: null_mut ( ) ,
42
- ) ;
43
- SCDynamicStore :: wrap_under_create_rule ( store)
44
- }
45
- }
46
-
47
- /// Creates a new session used to interact with the dynamic store maintained by the System
48
- /// Configuration server. Uses [`SCDynamicStoreCreateWithOptions`] underneath and sets
49
- /// `kSCDynamicStoreUseSessionKeys` to true.
50
- ///
51
- /// [`SCDynamicStoreCreateWithOptions`]: https://developer.apple.com/documentation/systemconfiguration/1437818-scdynamicstorecreatewithoptions?language=objc
52
- pub fn create_with_session_keys < S : Into < CFString > > ( name : S ) -> Self {
53
- let cf_name = name. into ( ) ;
54
- unsafe {
55
- let store_options = CFDictionary :: from_CFType_pairs ( & [
56
- (
57
- CFString :: wrap_under_create_rule ( kSCDynamicStoreUseSessionKeys) ,
58
- CFBoolean :: true_value ( ) ,
59
- ) ,
60
- ] ) ;
61
160
let store = SCDynamicStoreCreateWithOptions (
62
161
kCFAllocatorDefault,
63
- cf_name . as_concrete_TypeRef ( ) ,
162
+ name . as_concrete_TypeRef ( ) ,
64
163
store_options. as_concrete_TypeRef ( ) ,
65
- None ,
66
- ptr :: null_mut ( ) ,
164
+ callout ,
165
+ context ,
67
166
) ;
68
167
SCDynamicStore :: wrap_under_create_rule ( store)
69
168
}
@@ -108,4 +207,53 @@ impl SCDynamicStore {
108
207
} ;
109
208
success != 0
110
209
}
210
+
211
+ /// Specifies a set of keys and key patterns that should be monitored for changes.
212
+ pub fn set_notification_keys < T1 , T2 > (
213
+ & self ,
214
+ keys : & CFArray < T1 > ,
215
+ patterns : & CFArray < T2 > ,
216
+ ) -> bool {
217
+ let success = unsafe {
218
+ SCDynamicStoreSetNotificationKeys (
219
+ self . as_concrete_TypeRef ( ) ,
220
+ keys. as_concrete_TypeRef ( ) ,
221
+ patterns. as_concrete_TypeRef ( ) ,
222
+ )
223
+ } ;
224
+ success != 0
225
+ }
226
+
227
+ /// Creates a run loop source object that can be added to the application's run loop.
228
+ pub fn create_run_loop_source ( & self ) -> CFRunLoopSource {
229
+ unsafe {
230
+ let run_loop_source_ref = SCDynamicStoreCreateRunLoopSource (
231
+ kCFAllocatorDefault,
232
+ self . as_concrete_TypeRef ( ) ,
233
+ 0 ,
234
+ ) ;
235
+ CFRunLoopSource :: wrap_under_create_rule ( run_loop_source_ref)
236
+ }
237
+ }
238
+ }
239
+
240
+
241
+ /// The raw callback used by the safe `SCDynamicStore` to convert from the `SCDynamicStoreCallBack`
242
+ /// to the `SCDynamicStoreCallBackT`
243
+ unsafe extern "C" fn convert_callback < T > (
244
+ store_ref : SCDynamicStoreRef ,
245
+ changed_keys_ref : CFArrayRef ,
246
+ context_ptr : * mut c_void ,
247
+ ) {
248
+ let store = SCDynamicStore :: wrap_under_get_rule ( store_ref) ;
249
+ let changed_keys = CFArray :: < CFString > :: wrap_under_get_rule ( changed_keys_ref) ;
250
+ let context = & mut * ( context_ptr as * mut _ as * mut SCDynamicStoreCallBackContext < T > ) ;
251
+
252
+ ( context. callout ) ( store, changed_keys, & mut context. info ) ;
253
+ }
254
+
255
+ // Release function called by core foundation on release of the dynamic store context.
256
+ unsafe extern "C" fn release_callback_context < T > ( context_ptr : * const c_void ) {
257
+ // Bring back the context object from raw ptr so it is correctly freed.
258
+ let _context = Box :: from_raw ( context_ptr as * mut SCDynamicStoreCallBackContext < T > ) ;
111
259
}
0 commit comments