2 * Copyright (c) 2012-2016 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>
26 #include <dispatch/dispatch.h>
28 #include <vproc_priv.h>
30 #include <xpc/private.h>
32 #include <CoreFoundation/CoreFoundation.h>
33 #include <SystemConfiguration/SCPrivate.h>
34 #include "libSystemConfiguration_server.h"
36 #define kTrailingEdgeAgentEntitlement "com.apple.SystemConfiguration.trailing-edge-agent"
40 os_log_t SC_LOG_HANDLE
;
41 #endif //SC_LOG_HANDLE
45 #pragma mark client connection trackng
49 xpc_connection_t connection
;
54 uint64_t generation_pushed
;
55 uint64_t generation_acknowledged
;
59 static __inline__ CF_RETURNS_RETAINED CFDataRef
60 _client_key(xpc_connection_t c
)
66 client_key
= CFDataCreate(NULL
, (UInt8
*)&key
, sizeof(key
));
72 _handle_entitlement_check_failure(pid_t pid
)
74 static Boolean cleanupScheduled
= FALSE
;
75 static dispatch_once_t initializer
= 0;
76 static CFMutableArrayRef pids
= NULL
;
77 static dispatch_queue_t queue
= NULL
;
79 dispatch_once(&initializer
, ^{
80 pids
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
81 queue
= dispatch_queue_create("handle unentitled ack", NULL
);
84 dispatch_sync(queue
, ^{
85 CFNumberRef pidNumber
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &pid
);
87 if (!CFArrayContainsValue(pids
, CFRangeMake(0, CFArrayGetCount(pids
)), pidNumber
)) {
88 CFArrayAppendValue(pids
, pidNumber
);
90 SC_log(LOG_INFO
, "DNS/nwi dropping ack w/no entitlement, pid = %d", pid
);
92 if (!cleanupScheduled
) {
93 cleanupScheduled
= TRUE
;
94 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 180LL * NSEC_PER_SEC
), queue
, ^{
95 CFArrayRemoveAllValues(pids
);
96 cleanupScheduled
= FALSE
;
101 CFRelease(pidNumber
);
107 * libSystemConfiguraiton_client
109 * - all APIs must be called from the same [serial] dispatch queue
115 _libSC_info_server_init(libSC_info_server_t
*server_info
) {
116 bzero(server_info
, sizeof(*server_info
));
117 server_info
->info
= CFDictionaryCreateMutable(NULL
,
119 &kCFTypeDictionaryKeyCallBacks
,
120 &kCFTypeDictionaryValueCallBacks
);
127 _libSC_info_server_set_data(libSC_info_server_t
*server_info
,
131 // update stored configuration
132 if (server_info
->data
!= NULL
) {
133 CFRelease(server_info
->data
);
134 server_info
->data
= NULL
;
138 server_info
->data
= data
;
142 if (generation
== 0) {
143 // generation must be non-zero
146 server_info
->generation
= generation
;
148 // new configuration, all ack'ing clients need to
150 server_info
->inSync_NO
+= server_info
->inSync_YES
;
151 server_info
->inSync_YES
= 0;
158 * _libSC_info_server_in_sync
160 * Called to check if all of the "active" configuration [XPC] connection
161 * are in sync with the requested generation.
165 _libSC_info_server_in_sync(libSC_info_server_t
*server_info
)
167 return (server_info
->inSync_NO
== 0) ? TRUE
: FALSE
;
172 * _libSC_info_server_open
174 * Called when a new configuration [XPC] connection
177 * - tracks the last generation pushed to the caller and
178 * the last generation ack'd by the caller
182 _libSC_info_server_open(libSC_info_server_t
*server_info
,
185 CFDataRef client_key
;
186 CFMutableDataRef client_val
;
189 client_key
= _client_key(c
);
191 client_val
= CFDataCreateMutable(NULL
, sizeof(*val
));
192 CFDataSetLength(client_val
, sizeof(*val
));
194 val
= (client_val_t
*)(void *)CFDataGetMutableBytePtr(client_val
);
195 val
->pid
= xpc_connection_get_pid(c
);
196 val
->generation_pushed
= 0;
197 val
->generation_acknowledged
= 0;
199 CFDictionarySetValue(server_info
->info
, client_key
, client_val
);
200 CFRelease(client_key
);
201 CFRelease(client_val
);
208 * _libSC_info_server_get_data
210 * Called when a [XPC] connection wants the current configuration.
212 * - updates the last generation pushed to the caller
216 _libSC_info_server_get_data(libSC_info_server_t
*server_info
,
218 uint64_t *generation
)
220 CFDataRef client_key
;
221 CFMutableDataRef client_val
;
224 // update last generation pushed to client
225 client_key
= _client_key(c
);
226 client_val
= (CFMutableDataRef
)CFDictionaryGetValue(server_info
->info
, client_key
);
227 CFRelease(client_key
);
229 val
= (client_val_t
*)(void *)CFDataGetMutableBytePtr(client_val
);
230 val
->generation_pushed
= server_info
->generation
;
233 *generation
= server_info
->generation
;
234 if (*generation
== 1) {
239 return server_info
->data
;
244 * _libSC_info_server_acknowledged
246 * Called when a [XPC] connection wants to acknowledge a
247 * processed configuration.
249 * - updates the last generation ack'd by the caller
250 * - updates the count of [XPC] connections that are / not in sync
254 _libSC_info_server_acknowledged(libSC_info_server_t
*server_info
,
258 CFDataRef client_key
;
259 CFMutableDataRef client_val
;
260 xpc_object_t ent_value
;
261 Boolean entitled
= FALSE
;
262 Boolean sync_updated
= FALSE
;
265 ent_value
= xpc_connection_copy_entitlement_value(c
, kTrailingEdgeAgentEntitlement
);
266 if (ent_value
!= NULL
) {
267 if (xpc_get_type(ent_value
) == XPC_TYPE_BOOL
) {
268 entitled
= xpc_bool_get_value(ent_value
);
270 xpc_release(ent_value
);
274 _handle_entitlement_check_failure(xpc_connection_get_pid(c
));
278 client_key
= _client_key(c
);
279 client_val
= (CFMutableDataRef
)CFDictionaryGetValue(server_info
->info
, client_key
);
280 CFRelease(client_key
);
282 val
= (client_val_t
*)(void *)CFDataGetMutableBytePtr(client_val
);
284 if (val
->generation_acknowledged
== 0) {
286 if (generation
== server_info
->generation
) {
287 server_info
->inSync_YES
++;
289 server_info
->inSync_NO
++;
292 } else if ((generation
!= val
->generation_acknowledged
) &&
293 (generation
== server_info
->generation
)) {
294 // if we've previously ack'd a configuration
295 // ... and if we are ack'ing a configuration
296 // that we have not previously ack'd
297 // ... and if we're ack'ing the current stored
299 server_info
->inSync_NO
--;
300 server_info
->inSync_YES
++;
304 val
->generation_acknowledged
= generation
;
311 * _libSC_info_server_close
313 * Called when a configuration [XPC] connection is closed.
317 _libSC_info_server_close(libSC_info_server_t
*server_info
,
320 CFDataRef client_key
;
321 CFMutableDataRef client_val
;
322 Boolean sync_updated
= FALSE
;
324 client_key
= _client_key(c
);
326 // get client info, remove ack'd info
327 client_val
= (CFMutableDataRef
)CFDictionaryGetValue(server_info
->info
, client_key
);
328 if (client_val
!= NULL
) {
331 val
= (client_val_t
*)(void *)CFDataGetMutableBytePtr(client_val
);
332 if (val
->generation_acknowledged
> 0) {
333 // if we've previously ack'd a configuration
334 if (val
->generation_acknowledged
== server_info
->generation
) {
335 // if currently in sync
336 server_info
->inSync_YES
--;
338 // if currently NOT in sync
339 server_info
->inSync_NO
--;
345 CFDictionaryRemoveValue(server_info
->info
, client_key
);
346 CFRelease(client_key
);