X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/1e2cbe6aa7461cd7abf510b7c26db26974657962..5e9ce69ef59d7d98ff13748ce18266968031faaf:/libSystemConfiguration/libSystemConfiguration_server.c diff --git a/libSystemConfiguration/libSystemConfiguration_server.c b/libSystemConfiguration/libSystemConfiguration_server.c new file mode 100644 index 0000000..82a201a --- /dev/null +++ b/libSystemConfiguration/libSystemConfiguration_server.c @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2012, 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "libSystemConfiguration_server.h" + +#define kTrailingEdgeAgentEntitlement "com.apple.SystemConfiguration.trailing-edge-agent" + +#pragma mark - +#pragma mark Support functions + + +//__private_extern__ void +//log_xpc_object(const char *msg, xpc_object_t obj) +//{ +// char *desc; +// +// desc = xpc_copy_description(obj); +// asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s = %s", msg, desc); +// free(desc); +//} + + +#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; +} + + +#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) +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); + + SCLog(TRUE, LOG_ERR, CFSTR("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); + }); +} +#endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) + + +/* + * libSystemConfiguraiton_client + * + * - all APIs must be called from the same [serial] dispatch queue + */ + + +__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) +{ + // update stored configuration + if (server_info->data != NULL) { + CFRelease(server_info->data); + server_info->data = NULL; + } + if (data != NULL) { + CFRetain(data); + server_info->data = data; + } + + // update generation + if (generation == 0) { + // generation must be non-zero + generation = 1; + } + server_info->generation = generation; + + // new configuration, all ack'ing clients need to + // check-in again + server_info->inSync_NO += server_info->inSync_YES; + server_info->inSync_YES = 0; + + return; +} + + +/* + * _libSC_info_server_in_sync + * + * Called to check if all of the "active" configuration [XPC] connection + * are in sync with the requested generation. + */ +__private_extern__ +Boolean +_libSC_info_server_in_sync(libSC_info_server_t *server_info) +{ + return (server_info->inSync_NO == 0) ? TRUE : FALSE; +} + + +/* + * _libSC_info_server_open + * + * Called when a new configuration [XPC] connection + * is established. + * + * - tracks the last generation pushed to the caller and + * the last generation ack'd by the caller + */ +__private_extern__ +void +_libSC_info_server_open(libSC_info_server_t *server_info, + xpc_connection_t c) +{ + CFDataRef client_key; + CFDataRef client_val; + client_val_t val; + + client_key = _client_key(c); + + val.pid = xpc_connection_get_pid(c); + val.generation_pushed = 0; + val.generation_acknowledged = 0; + client_val = CFDataCreate(NULL, (UInt8 *)&val, sizeof(val)); + + CFDictionarySetValue(server_info->info, client_key, client_val); + CFRelease(client_key); + CFRelease(client_val); + + return; +} + + +/* + * _libSC_info_server_get_data + * + * Called when a [XPC] connection wants the current configuration. + * + * - updates the last generation pushed to the caller + */ +__private_extern__ +CFDataRef +_libSC_info_server_get_data(libSC_info_server_t *server_info, + xpc_connection_t c, + uint64_t *generation) +{ + CFDataRef client_key; + CFDataRef client_val; + client_val_t *val; + + // update last generation pushed to client + client_key = _client_key(c); + client_val = CFDictionaryGetValue(server_info->info, client_key); + CFRelease(client_key); + + val = (client_val_t *)(void *)CFDataGetBytePtr(client_val); + val->generation_pushed = server_info->generation; + + // return generation + *generation = server_info->generation; + if (*generation == 1) { + *generation = 0; + } + + // return data + return server_info->data; +} + + +/* + * _libSC_info_server_acknowledged + * + * Called when a [XPC] connection wants to acknowledge a + * processed configuration. + * + * - updates the last generation ack'd by the caller + * - updates the count of [XPC] connections that are / not in sync + */ +__private_extern__ +Boolean +_libSC_info_server_acknowledged(libSC_info_server_t *server_info, + xpc_connection_t c, + uint64_t generation) +{ + CFDataRef client_key; + CFDataRef client_val; +#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) + xpc_object_t ent_value; + Boolean entitled = FALSE; +#endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) + Boolean sync_updated = FALSE; + client_val_t *val; + +#if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) + 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; + } +#endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000)) + + client_key = _client_key(c); + client_val = CFDictionaryGetValue(server_info->info, client_key); + CFRelease(client_key); + + val = (client_val_t *)(void *)CFDataGetBytePtr(client_val); + + if (val->generation_acknowledged == 0) { + // if first ack + 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)) { + // if we've previously ack'd a configuration + // ... and if we are ack'ing a configuration + // that we have not previously ack'd + // ... and if we're ack'ing the current stored + // configuration + server_info->inSync_NO--; + server_info->inSync_YES++; + sync_updated = TRUE; + } + + val->generation_acknowledged = generation; + + return sync_updated; +} + + +/* + * _libSC_info_server_close + * + * Called when a configuration [XPC] connection is closed. + */ +__private_extern__ +Boolean +_libSC_info_server_close(libSC_info_server_t *server_info, + xpc_connection_t c) +{ + CFDataRef client_key; + CFDataRef client_val; + Boolean sync_updated = FALSE; + + client_key = _client_key(c); + + // get client info, remove ack'd info + client_val = CFDictionaryGetValue(server_info->info, client_key); + if (client_val != NULL) { + client_val_t *val; + + val = (client_val_t *)(void *)CFDataGetBytePtr(client_val); + if (val->generation_acknowledged > 0) { + // if we've previously ack'd a configuration + if (val->generation_acknowledged == server_info->generation) { + // if currently in sync + server_info->inSync_YES--; + } else { + // if currently NOT in sync + server_info->inSync_NO--; + sync_updated = TRUE; + } + } + } + + CFDictionaryRemoveValue(server_info->info, client_key); + CFRelease(client_key); + + return sync_updated; +} +