]> git.saurik.com Git - apple/configd.git/blobdiff - libSystemConfiguration/libSystemConfiguration_server.c
configd-596.12.tar.gz
[apple/configd.git] / libSystemConfiguration / libSystemConfiguration_server.c
diff --git a/libSystemConfiguration/libSystemConfiguration_server.c b/libSystemConfiguration/libSystemConfiguration_server.c
new file mode 100644 (file)
index 0000000..82a201a
--- /dev/null
@@ -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 <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
+
+
+//__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;
+}
+