SCNetworkInterfaceProvider.c [plain text]
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFRuntime.h>
#include <libkern/OSAtomic.h>
#include "SCNetworkConfigurationPrivate.h"
#include "SCNetworkConfigurationInternal.h"
#include "SCNetworkInterfaceProvider.h"
static void
my_CFRelease(void * t)
{
void * * obj = (void * *)t;
if (obj && *obj) {
CFRelease(*obj);
*obj = NULL;
}
return;
}
typedef struct {
const void * obj;
int32_t retain_count;
} ObjectWrapper, * ObjectWrapperRef;
static const void *
ObjectWrapperRetain(const void * info)
{
ObjectWrapperRef wrapper = (ObjectWrapperRef)info;
(void)OSAtomicIncrement32(&wrapper->retain_count);
return (info);
}
static ObjectWrapperRef
ObjectWrapperAllocate(const void * obj)
{
ObjectWrapperRef wrapper;
wrapper = (ObjectWrapperRef)malloc(sizeof(*wrapper));
wrapper->obj = obj;
wrapper->retain_count = 1;
return (wrapper);
}
static void
ObjectWrapperRelease(const void * info)
{
int32_t new_val;
ObjectWrapperRef wrapper = (ObjectWrapperRef)info;
new_val = OSAtomicDecrement32(&wrapper->retain_count);
if (new_val == 0) {
free(wrapper);
}
else {
assert(new_val > 0);
}
return;
}
static void
ObjectWrapperSetObject(ObjectWrapperRef wrapper, const void * obj)
{
wrapper->obj = obj;
}
static const void *
ObjectWrapperGetObject(ObjectWrapperRef wrapper)
{
return (wrapper->obj);
}
static SCDynamicStoreRef
StoreObjectWrapperAllocate(const void * obj,
CFStringRef name,
SCDynamicStoreCallBack handler,
CFArrayRef keys,
CFArrayRef patterns,
dispatch_queue_t queue,
ObjectWrapperRef * ret_wrapper)
{
SCDynamicStoreContext context = {
.version = 0,
.info = NULL,
.retain = ObjectWrapperRetain,
.release = ObjectWrapperRelease,
.copyDescription = NULL
};
SCDynamicStoreRef store;
ObjectWrapperRef wrapper;
wrapper = ObjectWrapperAllocate(obj);
context.info = wrapper;
store = SCDynamicStoreCreate(NULL, name, handler, &context);
if (store == NULL) {
SC_log(LOG_NOTICE,
"%@: SCDynamicStoreCreate failed", name);
}
else if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) {
SC_log(LOG_NOTICE,
"%@: SCDynamicStoreSetNoticationKeys failed", name);
CFRelease(store);
store = NULL;
}
else if (queue != NULL
&& !SCDynamicStoreSetDispatchQueue(store, queue)) {
SC_log(LOG_NOTICE,
"%@: SCDynamicStoreSetDispatchQueue failed", name);
CFRelease(store);
store = NULL;
}
if (store == NULL) {
ObjectWrapperRelease(wrapper);
wrapper = NULL;
}
*ret_wrapper = wrapper;
return (store);
}
static CFStringRef __SCNetworkInterfaceProviderCopyDebugDesc(CFTypeRef cf);
static void __SCNetworkInterfaceProviderDeallocate(CFTypeRef cf);
static CFTypeID __kSCNetworkInterfaceProviderTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __SCNetworkInterfaceProviderClass = {
0,
"SCNetworkInterfaceProvider",
NULL,
NULL,
__SCNetworkInterfaceProviderDeallocate,
NULL,
NULL,
NULL,
__SCNetworkInterfaceProviderCopyDebugDesc
};
struct __SCNetworkInterfaceProvider {
CFRuntimeBase cf_base;
IPMonitorControlRef control;
SCDynamicStoreRef store;
ObjectWrapperRef wrapper;
dispatch_queue_t queue;
SCNetworkInterfaceProviderEventHandler handler;
CFStringRef if_name;
SCNetworkInterfaceRef if_type;
Boolean enabled;
Boolean needed;
};
static CFStringRef
__SCNetworkInterfaceProviderCopyDebugDesc(CFTypeRef cf)
{
CFAllocatorRef allocator = CFGetAllocator(cf);
SCNetworkInterfaceProviderRef provider = (SCNetworkInterfaceProviderRef)cf;
CFMutableStringRef result;
result = CFStringCreateMutable(allocator, 0);
CFStringAppendFormat(result, NULL,
CFSTR("<SCNetworkInterfaceProvider %@ %@ <%p>"),
provider->if_type, provider->if_name, cf);
return (result);
}
static void
SCNetworkInterfaceProviderDeallocate(SCNetworkInterfaceProviderRef provider)
{
provider->enabled = FALSE;
my_CFRelease(&provider->control);
if (provider->wrapper != NULL) {
ObjectWrapperSetObject(provider->wrapper, NULL);
ObjectWrapperRelease(provider->wrapper);
provider->wrapper = NULL;
}
if (provider->store != NULL) {
SCDynamicStoreSetDispatchQueue(provider->store, NULL);
my_CFRelease(&provider->store);
}
if (provider->queue != NULL) {
dispatch_release(provider->queue);
provider->queue = NULL;
}
if (provider->handler != NULL) {
Block_release(provider->handler);
provider->handler = NULL;
}
my_CFRelease(&provider->if_name);
my_CFRelease(&provider->if_type);
}
static void
__SCNetworkInterfaceProviderDeallocate(CFTypeRef cf)
{
SCNetworkInterfaceProviderRef provider = (SCNetworkInterfaceProviderRef)cf;
if (provider->queue != NULL) {
dispatch_sync(provider->queue, ^{
SCNetworkInterfaceProviderDeallocate(provider);
});
}
else {
SCNetworkInterfaceProviderDeallocate(provider);
}
return;
}
static void
__SCNetworkInterfaceProviderRegisterClass(void)
{
static dispatch_once_t once;
dispatch_block_t once_block;
once_block = ^{
__kSCNetworkInterfaceProviderTypeID
= _CFRuntimeRegisterClass(&__SCNetworkInterfaceProviderClass);
};
dispatch_once(&once, once_block);
return;
}
static SCNetworkInterfaceProviderRef
__SCNetworkInterfaceProviderAllocate(CFAllocatorRef allocator)
{
SCNetworkInterfaceProviderRef provider;
int size;
__SCNetworkInterfaceProviderRegisterClass();
size = sizeof(*provider) - sizeof(CFRuntimeBase);
provider = (SCNetworkInterfaceProviderRef)
_CFRuntimeCreateInstance(allocator,
__kSCNetworkInterfaceProviderTypeID,
size, NULL);
memset(((void *)provider) + sizeof(CFRuntimeBase), 0, size);
return (provider);
}
static void
SCNetworkInterfaceProviderCheck(SCNetworkInterfaceProviderRef provider)
{
Boolean advisory_set;
if (!provider->enabled || provider->handler == NULL) {
return;
}
advisory_set
= IPMonitorControlAnyInterfaceAdvisoryIsSet(provider->control);
if (provider->needed != advisory_set) {
SCNetworkInterfaceProviderEvent event;
event = advisory_set
? kSCNetworkInterfaceProviderEventActivationRequested
: kSCNetworkInterfaceProviderEventActivationNoLongerRequested;
(provider->handler)(event, NULL);
provider->needed = advisory_set;
}
return;
}
static void
StoreHandleChanges(SCDynamicStoreRef store, CFArrayRef changes, void * info)
{
#pragma unused(store)
#pragma unused(changes)
SCNetworkInterfaceProviderRef provider;
ObjectWrapperRef wrapper = (ObjectWrapperRef)info;
provider = (SCNetworkInterfaceProviderRef)ObjectWrapperGetObject(wrapper);
if (provider == NULL) {
return;
}
SCNetworkInterfaceProviderCheck(provider);
return;
}
SCNetworkInterfaceProviderRef
SCNetworkInterfaceProviderCreate(CFStringRef type,
CFStringRef ifname,
CFDictionaryRef options)
{
IPMonitorControlRef control;
CFStringRef pattern;
CFArrayRef patterns;
SCNetworkInterfaceProviderRef provider;
dispatch_queue_t queue;
SCDynamicStoreRef store = NULL;
ObjectWrapperRef wrapper = NULL;
if (options != NULL || ifname == NULL || type == NULL) {
_SCErrorSet(kSCStatusInvalidArgument);
return (NULL);
}
control = IPMonitorControlCreate();
if (control == NULL) {
_SCErrorSet(kSCStatusFailed);
return (NULL);
}
pattern
= IPMonitorControlCopyInterfaceAdvisoryNotificationKey(kSCCompAnyRegex);
patterns = CFArrayCreate(NULL, (const void * *)&pattern, 1,
&kCFTypeArrayCallBacks);
CFRelease(pattern);
#define OUR_NAME "SCNetworkInterfaceProvider"
queue = dispatch_queue_create(OUR_NAME, NULL);
provider = __SCNetworkInterfaceProviderAllocate(NULL);
store = StoreObjectWrapperAllocate(provider,
CFSTR(OUR_NAME),
StoreHandleChanges,
NULL,
patterns,
queue,
&wrapper);
CFRelease(patterns);
if (store == NULL) {
dispatch_release(queue);
CFRelease(provider);
provider = NULL;
CFRelease(control);
}
else {
provider->control = control;
provider->store = store;
provider->wrapper = wrapper;
provider->queue = queue;
provider->if_name = CFRetain(ifname);
provider->if_type = CFRetain(type);
}
return (provider);
}
void
SCNetworkInterfaceProviderSetEventHandler(SCNetworkInterfaceProviderRef provider,
SCNetworkInterfaceProviderEventHandler handler)
{
if (handler == NULL) {
return;
}
dispatch_sync(provider->queue, ^{
if (provider->enabled) {
SC_log(LOG_NOTICE,
"%s: call SCNetworkInterfaceSetEventHandler before "
" SCNetworkInterfaceProviderResume", __FUNCTION__);
return;
}
if (provider->handler != NULL) {
SC_log(LOG_NOTICE,
"%s: ignoring second invocation of "
"SCNetworkInterfaceSetEventHandler", __FUNCTION__);
return;
}
provider->handler = Block_copy(handler);
});
return;
}
void
SCNetworkInterfaceProviderResume(SCNetworkInterfaceProviderRef provider)
{
dispatch_async(provider->queue, ^{
if (!provider->enabled) {
provider->enabled = TRUE;
SCNetworkInterfaceProviderCheck(provider);
}
});
return;
}
#if TEST_SCNetworkInterfaceProvider
__private_extern__ os_log_t
__log_SCNetworkInterfaceProvider(void)
{
static os_log_t log = NULL;
if (log == NULL) {
log = os_log_create("com.apple.SystemConfiguration", "SCNetworkConfiguration");
}
return log;
}
static void
event_handler(SCNetworkInterfaceProviderRef provider,
SCNetworkInterfaceProviderEvent event,
CFDictionaryRef event_data)
{
printf("<%p> event %d\n", provider, event);
}
int
main(int argc, char * argv[])
{
SCNetworkInterfaceProviderEventHandler handler;
SCNetworkInterfaceProviderRef provider;
provider
= SCNetworkInterfaceProviderCreate(kSCNetworkInterfaceTypeWWAN,
CFSTR("pdp_ip10"),
NULL);
if (provider == NULL) {
fprintf(stderr, "SCNetworkInterfaceProviderCreate failed\n");
exit(1);
}
handler = ^(SCNetworkInterfaceProviderEvent event,
CFDictionaryRef event_data) {
event_handler(provider, event, event_data);
};
SCNetworkInterfaceProviderSetEventHandler(provider, handler);
SCNetworkInterfaceProviderResume(provider);
dispatch_main();
exit(0);
return (0);
}
#endif