blob: d145c2a6e1b2e1a5e0219db48557dc8fb62878fa [file] [log] [blame]
use core_foundation::{
array::CFArray,
base::{CFType, TCFType, ToVoid},
dictionary::CFDictionary,
propertylist::CFPropertyList,
runloop::{kCFRunLoopCommonModes, CFRunLoop},
string::CFString,
};
use system_configuration::{
dynamic_store::{SCDynamicStore, SCDynamicStoreBuilder, SCDynamicStoreCallBackContext},
sys::schema_definitions::kSCPropNetDNSServerAddresses,
};
// This example will watch the dynamic store for changes to any DNS setting. As soon as a change
// is detected, it will be printed to stdout.
fn main() {
let callback_context = SCDynamicStoreCallBackContext {
callout: my_callback,
info: Context { call_count: 0 },
};
let store = SCDynamicStoreBuilder::new("my-watch-dns-store")
.callback_context(callback_context)
.build();
let watch_keys: CFArray<CFString> = CFArray::from_CFTypes(&[]);
let watch_patterns =
CFArray::from_CFTypes(&[CFString::from("(State|Setup):/Network/Service/.*/DNS")]);
if store.set_notification_keys(&watch_keys, &watch_patterns) {
println!("Registered for notifications");
} else {
panic!("Unable to register notifications");
}
let run_loop_source = store.create_run_loop_source();
let run_loop = CFRunLoop::get_current();
run_loop.add_source(&run_loop_source, unsafe { kCFRunLoopCommonModes });
println!("Entering run loop");
CFRunLoop::run_current();
}
/// This struct acts as a user provided context/payload to each notification callback.
/// Here one can store any type of data or state needed in the callback function.
#[derive(Debug)]
struct Context {
call_count: u64,
}
#[allow(clippy::needless_pass_by_value)]
fn my_callback(store: SCDynamicStore, changed_keys: CFArray<CFString>, context: &mut Context) {
context.call_count += 1;
println!("Callback call count: {}", context.call_count);
for key in changed_keys.iter() {
if let Some(addresses) = get_dns(&store, key.clone()) {
println!("{} changed DNS to {:?}", *key, addresses);
} else {
println!("{} removed DNS", *key);
}
}
}
fn get_dns(store: &SCDynamicStore, path: CFString) -> Option<Vec<String>> {
let dns_settings = store
.get(path)
.and_then(CFPropertyList::downcast_into::<CFDictionary>)?;
let address_array = dns_settings
.find(unsafe { kSCPropNetDNSServerAddresses }.to_void())
.map(|ptr| unsafe { CFType::wrap_under_get_rule(*ptr) })
.and_then(CFType::downcast_into::<CFArray>)?;
let mut result = Vec::with_capacity(address_array.len() as usize);
for address_ptr in &address_array {
let address =
unsafe { CFType::wrap_under_get_rule(*address_ptr) }.downcast_into::<CFString>()?;
result.push(address.to_string())
}
Some(result)
}