2 * Copyright (c) 2012, 2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <Availability.h>
25 #include <TargetConditionals.h>
27 #include <dispatch/dispatch.h>
29 #include <vproc_priv.h>
31 #include <xpc/private.h>
33 #include <CoreFoundation/CoreFoundation.h>
34 #include <SystemConfiguration/SCPrivate.h>
36 #include "libSystemConfiguration_server.h"
38 #define kTrailingEdgeAgentEntitlement "com.apple.SystemConfiguration.trailing-edge-agent"
41 #pragma mark Support functions
44 //__private_extern__ void
45 //log_xpc_object(const char *msg, xpc_object_t obj)
49 // desc = xpc_copy_description(obj);
50 // asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s = %s", msg, desc);
56 #pragma mark client connection trackng
60 xpc_connection_t connection
;
65 uint64_t generation_pushed
;
66 uint64_t generation_acknowledged
;
70 static __inline__ CF_RETURNS_RETAINED CFDataRef
71 _client_key(xpc_connection_t c
)
77 client_key
= CFDataCreate(NULL
, (UInt8
*)&key
, sizeof(key
));
82 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
84 _handle_entitlement_check_failure(pid_t pid
)
86 static Boolean cleanupScheduled
= FALSE
;
87 static dispatch_once_t initializer
= 0;
88 static CFMutableArrayRef pids
= NULL
;
89 static dispatch_queue_t queue
= NULL
;
91 dispatch_once(&initializer
, ^{
92 pids
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
93 queue
= dispatch_queue_create("handle unentitled ack", NULL
);
96 dispatch_sync(queue
, ^{
97 CFNumberRef pidNumber
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &pid
);
99 if (!CFArrayContainsValue(pids
, CFRangeMake(0, CFArrayGetCount(pids
)), pidNumber
)) {
100 CFArrayAppendValue(pids
, pidNumber
);
102 SCLog(TRUE
, LOG_ERR
, CFSTR("DNS/nwi dropping ack w/no entitlement, pid = %d"), pid
);
104 if (!cleanupScheduled
) {
105 cleanupScheduled
= TRUE
;
106 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 180LL * NSEC_PER_SEC
), queue
, ^{
107 CFArrayRemoveAllValues(pids
);
108 cleanupScheduled
= FALSE
;
113 CFRelease(pidNumber
);
116 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
120 * libSystemConfiguraiton_client
122 * - all APIs must be called from the same [serial] dispatch queue
128 _libSC_info_server_init(libSC_info_server_t
*server_info
) {
129 bzero(server_info
, sizeof(*server_info
));
130 server_info
->info
= CFDictionaryCreateMutable(NULL
,
132 &kCFTypeDictionaryKeyCallBacks
,
133 &kCFTypeDictionaryValueCallBacks
);
140 _libSC_info_server_set_data(libSC_info_server_t
*server_info
,
144 // update stored configuration
145 if (server_info
->data
!= NULL
) {
146 CFRelease(server_info
->data
);
147 server_info
->data
= NULL
;
151 server_info
->data
= data
;
155 if (generation
== 0) {
156 // generation must be non-zero
159 server_info
->generation
= generation
;
161 // new configuration, all ack'ing clients need to
163 server_info
->inSync_NO
+= server_info
->inSync_YES
;
164 server_info
->inSync_YES
= 0;
171 * _libSC_info_server_in_sync
173 * Called to check if all of the "active" configuration [XPC] connection
174 * are in sync with the requested generation.
178 _libSC_info_server_in_sync(libSC_info_server_t
*server_info
)
180 return (server_info
->inSync_NO
== 0) ? TRUE
: FALSE
;
185 * _libSC_info_server_open
187 * Called when a new configuration [XPC] connection
190 * - tracks the last generation pushed to the caller and
191 * the last generation ack'd by the caller
195 _libSC_info_server_open(libSC_info_server_t
*server_info
,
198 CFDataRef client_key
;
199 CFDataRef client_val
;
202 client_key
= _client_key(c
);
204 val
.pid
= xpc_connection_get_pid(c
);
205 val
.generation_pushed
= 0;
206 val
.generation_acknowledged
= 0;
207 client_val
= CFDataCreate(NULL
, (UInt8
*)&val
, sizeof(val
));
209 CFDictionarySetValue(server_info
->info
, client_key
, client_val
);
210 CFRelease(client_key
);
211 CFRelease(client_val
);
218 * _libSC_info_server_get_data
220 * Called when a [XPC] connection wants the current configuration.
222 * - updates the last generation pushed to the caller
226 _libSC_info_server_get_data(libSC_info_server_t
*server_info
,
228 uint64_t *generation
)
230 CFDataRef client_key
;
231 CFDataRef client_val
;
234 // update last generation pushed to client
235 client_key
= _client_key(c
);
236 client_val
= CFDictionaryGetValue(server_info
->info
, client_key
);
237 CFRelease(client_key
);
239 val
= (client_val_t
*)(void *)CFDataGetBytePtr(client_val
);
240 val
->generation_pushed
= server_info
->generation
;
243 *generation
= server_info
->generation
;
244 if (*generation
== 1) {
249 return server_info
->data
;
254 * _libSC_info_server_acknowledged
256 * Called when a [XPC] connection wants to acknowledge a
257 * processed configuration.
259 * - updates the last generation ack'd by the caller
260 * - updates the count of [XPC] connections that are / not in sync
264 _libSC_info_server_acknowledged(libSC_info_server_t
*server_info
,
268 CFDataRef client_key
;
269 CFDataRef client_val
;
270 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
271 xpc_object_t ent_value
;
272 Boolean entitled
= FALSE
;
273 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
274 Boolean sync_updated
= FALSE
;
277 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
278 ent_value
= xpc_connection_copy_entitlement_value(c
, kTrailingEdgeAgentEntitlement
);
279 if (ent_value
!= NULL
) {
280 if (xpc_get_type(ent_value
) == XPC_TYPE_BOOL
) {
281 entitled
= xpc_bool_get_value(ent_value
);
283 xpc_release(ent_value
);
287 _handle_entitlement_check_failure(xpc_connection_get_pid(c
));
290 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
292 client_key
= _client_key(c
);
293 client_val
= CFDictionaryGetValue(server_info
->info
, client_key
);
294 CFRelease(client_key
);
296 val
= (client_val_t
*)(void *)CFDataGetBytePtr(client_val
);
298 if (val
->generation_acknowledged
== 0) {
300 if (generation
== server_info
->generation
) {
301 server_info
->inSync_YES
++;
303 server_info
->inSync_NO
++;
306 } else if ((generation
!= val
->generation_acknowledged
) &&
307 (generation
== server_info
->generation
)) {
308 // if we've previously ack'd a configuration
309 // ... and if we are ack'ing a configuration
310 // that we have not previously ack'd
311 // ... and if we're ack'ing the current stored
313 server_info
->inSync_NO
--;
314 server_info
->inSync_YES
++;
318 val
->generation_acknowledged
= generation
;
325 * _libSC_info_server_close
327 * Called when a configuration [XPC] connection is closed.
331 _libSC_info_server_close(libSC_info_server_t
*server_info
,
334 CFDataRef client_key
;
335 CFDataRef client_val
;
336 Boolean sync_updated
= FALSE
;
338 client_key
= _client_key(c
);
340 // get client info, remove ack'd info
341 client_val
= CFDictionaryGetValue(server_info
->info
, client_key
);
342 if (client_val
!= NULL
) {
345 val
= (client_val_t
*)(void *)CFDataGetBytePtr(client_val
);
346 if (val
->generation_acknowledged
> 0) {
347 // if we've previously ack'd a configuration
348 if (val
->generation_acknowledged
== server_info
->generation
) {
349 // if currently in sync
350 server_info
->inSync_YES
--;
352 // if currently NOT in sync
353 server_info
->inSync_NO
--;
359 CFDictionaryRemoveValue(server_info
->info
, client_key
);
360 CFRelease(client_key
);