libSystemConfiguration_server.c [plain text]
#include <Availability.h>
#include <TargetConditionals.h>
#include <asl.h>
#include <dispatch/dispatch.h>
#include <vproc.h>
#include <vproc_priv.h>
#include <xpc/xpc.h>
#include <xpc/private.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SCPrivate.h>
#include "libSystemConfiguration_server.h"
#define kTrailingEdgeAgentEntitlement "com.apple.SystemConfiguration.trailing-edge-agent"
#pragma mark -
#pragma mark Support functions
#pragma mark -
#pragma mark client connection trackng
typedef struct {
xpc_connection_t connection;
} client_key_t;
typedef struct {
pid_t pid;
uint64_t generation_pushed;
uint64_t generation_acknowledged;
} client_val_t;
static __inline__ CF_RETURNS_RETAINED CFDataRef
_client_key(xpc_connection_t c)
{
client_key_t key;
CFDataRef client_key;
key.connection = c;
client_key = CFDataCreate(NULL, (UInt8 *)&key, sizeof(key));
return client_key;
}
static void
_handle_entitlement_check_failure(pid_t pid)
{
static Boolean cleanupScheduled = FALSE;
static dispatch_once_t initializer = 0;
static CFMutableArrayRef pids = NULL;
static dispatch_queue_t queue = NULL;
dispatch_once(&initializer, ^{
pids = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
queue = dispatch_queue_create("handle unentitled ack", NULL);
});
dispatch_sync(queue, ^{
CFNumberRef pidNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid);
if (!CFArrayContainsValue(pids, CFRangeMake(0, CFArrayGetCount(pids)), pidNumber)) {
CFArrayAppendValue(pids, pidNumber);
SC_log(LOG_INFO, "DNS/nwi dropping ack w/no entitlement, pid = %d", pid);
if (!cleanupScheduled) {
cleanupScheduled = TRUE;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 180LL * NSEC_PER_SEC), queue, ^{
CFArrayRemoveAllValues(pids);
cleanupScheduled = FALSE;
});
}
}
CFRelease(pidNumber);
});
}
__private_extern__
void
_libSC_info_server_init(libSC_info_server_t *server_info) {
bzero(server_info, sizeof(*server_info));
server_info->info = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
return;
}
__private_extern__
void
_libSC_info_server_set_data(libSC_info_server_t *server_info,
CFDataRef data,
uint64_t generation)
{
if (server_info->data != NULL) {
CFRelease(server_info->data);
server_info->data = NULL;
}
if (data != NULL) {
CFRetain(data);
server_info->data = data;
}
if (generation == 0) {
generation = 1;
}
server_info->generation = generation;
server_info->inSync_NO += server_info->inSync_YES;
server_info->inSync_YES = 0;
return;
}
__private_extern__
Boolean
_libSC_info_server_in_sync(libSC_info_server_t *server_info)
{
return (server_info->inSync_NO == 0) ? TRUE : FALSE;
}
__private_extern__
void
_libSC_info_server_open(libSC_info_server_t *server_info,
xpc_connection_t c)
{
CFDataRef client_key;
CFMutableDataRef client_val;
client_val_t *val;
client_key = _client_key(c);
client_val = CFDataCreateMutable(NULL, sizeof(*val));
CFDataSetLength(client_val, sizeof(*val));
val = (client_val_t *)(void *)CFDataGetMutableBytePtr(client_val);
val->pid = xpc_connection_get_pid(c);
val->generation_pushed = 0;
val->generation_acknowledged = 0;
CFDictionarySetValue(server_info->info, client_key, client_val);
CFRelease(client_key);
CFRelease(client_val);
return;
}
__private_extern__
CFDataRef
_libSC_info_server_get_data(libSC_info_server_t *server_info,
xpc_connection_t c,
uint64_t *generation)
{
CFDataRef client_key;
CFMutableDataRef client_val;
client_val_t *val;
client_key = _client_key(c);
client_val = (CFMutableDataRef)CFDictionaryGetValue(server_info->info, client_key);
CFRelease(client_key);
val = (client_val_t *)(void *)CFDataGetMutableBytePtr(client_val);
val->generation_pushed = server_info->generation;
*generation = server_info->generation;
if (*generation == 1) {
*generation = 0;
}
return server_info->data;
}
__private_extern__
Boolean
_libSC_info_server_acknowledged(libSC_info_server_t *server_info,
xpc_connection_t c,
uint64_t generation)
{
CFDataRef client_key;
CFMutableDataRef client_val;
xpc_object_t ent_value;
Boolean entitled = FALSE;
Boolean sync_updated = FALSE;
client_val_t *val;
ent_value = xpc_connection_copy_entitlement_value(c, kTrailingEdgeAgentEntitlement);
if (ent_value != NULL) {
if (xpc_get_type(ent_value) == XPC_TYPE_BOOL) {
entitled = xpc_bool_get_value(ent_value);
}
xpc_release(ent_value);
}
if (!entitled) {
_handle_entitlement_check_failure(xpc_connection_get_pid(c));
return FALSE;
}
client_key = _client_key(c);
client_val = (CFMutableDataRef)CFDictionaryGetValue(server_info->info, client_key);
CFRelease(client_key);
val = (client_val_t *)(void *)CFDataGetMutableBytePtr(client_val);
if (val->generation_acknowledged == 0) {
if (generation == server_info->generation) {
server_info->inSync_YES++;
} else {
server_info->inSync_NO++;
sync_updated = TRUE;
}
} else if ((generation != val->generation_acknowledged) &&
(generation == server_info->generation)) {
server_info->inSync_NO--;
server_info->inSync_YES++;
sync_updated = TRUE;
}
val->generation_acknowledged = generation;
return sync_updated;
}
__private_extern__
Boolean
_libSC_info_server_close(libSC_info_server_t *server_info,
xpc_connection_t c)
{
CFDataRef client_key;
CFMutableDataRef client_val;
Boolean sync_updated = FALSE;
client_key = _client_key(c);
client_val = (CFMutableDataRef)CFDictionaryGetValue(server_info->info, client_key);
if (client_val != NULL) {
client_val_t *val;
val = (client_val_t *)(void *)CFDataGetMutableBytePtr(client_val);
if (val->generation_acknowledged > 0) {
if (val->generation_acknowledged == server_info->generation) {
server_info->inSync_YES--;
} else {
server_info->inSync_NO--;
sync_updated = TRUE;
}
}
}
CFDictionaryRemoveValue(server_info->info, client_key);
CFRelease(client_key);
return sync_updated;
}