2 * Copyright (c) 2012-2018 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 <TargetConditionals.h>
25 #include <dispatch/dispatch.h>
27 #include <vproc_priv.h>
29 #include <xpc/private.h>
31 #include <CoreFoundation/CoreFoundation.h>
32 #include <SystemConfiguration/SCPrivate.h>
33 #include "libSystemConfiguration_server.h"
35 #define kTrailingEdgeAgentEntitlement "com.apple.SystemConfiguration.trailing-edge-agent"
39 os_log_t
SC_LOG_HANDLE(void);
40 #endif //SC_LOG_HANDLE
44 #pragma mark client connection trackng
48 xpc_connection_t connection
;
53 uint64_t generation_pushed
;
54 uint64_t generation_acknowledged
;
58 static __inline__ CF_RETURNS_RETAINED CFDataRef
59 _client_key(xpc_connection_t c
)
65 client_key
= CFDataCreate(NULL
, (UInt8
*)&key
, sizeof(key
));
71 _handle_entitlement_check_failure(pid_t pid
)
73 static Boolean cleanupScheduled
= FALSE
;
74 static dispatch_once_t initializer
= 0;
75 static CFMutableArrayRef pids
= NULL
;
76 static dispatch_queue_t queue
= NULL
;
78 dispatch_once(&initializer
, ^{
79 pids
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
80 queue
= dispatch_queue_create("handle unentitled ack", NULL
);
83 dispatch_sync(queue
, ^{
84 CFNumberRef pidNumber
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
, &pid
);
86 if (!CFArrayContainsValue(pids
, CFRangeMake(0, CFArrayGetCount(pids
)), pidNumber
)) {
87 CFArrayAppendValue(pids
, pidNumber
);
89 SC_log(LOG_INFO
, "DNS/nwi dropping ack w/no entitlement, pid = %d", pid
);
91 if (!cleanupScheduled
) {
92 cleanupScheduled
= TRUE
;
93 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 180LL * NSEC_PER_SEC
), queue
, ^{
94 CFArrayRemoveAllValues(pids
);
95 cleanupScheduled
= FALSE
;
100 CFRelease(pidNumber
);
106 * libSystemConfiguraiton_client
108 * - all APIs must be called from the same [serial] dispatch queue
114 _libSC_info_server_init(libSC_info_server_t
*server_info
) {
115 memset(server_info
, 0, sizeof(*server_info
));
116 server_info
->info
= CFDictionaryCreateMutable(NULL
,
118 &kCFTypeDictionaryKeyCallBacks
,
119 &kCFTypeDictionaryValueCallBacks
);
126 _libSC_info_server_set_data(libSC_info_server_t
*server_info
,
130 // update stored configuration
131 if (server_info
->data
!= NULL
) {
132 CFRelease(server_info
->data
);
133 server_info
->data
= NULL
;
137 server_info
->data
= data
;
141 if (generation
== 0) {
142 // generation must be non-zero
145 server_info
->generation
= generation
;
147 // new configuration, all ack'ing clients need to
149 server_info
->inSync_NO
+= server_info
->inSync_YES
;
150 server_info
->inSync_YES
= 0;
157 * _libSC_info_server_in_sync
159 * Called to check if all of the "active" configuration [XPC] connection
160 * are in sync with the requested generation.
164 _libSC_info_server_in_sync(libSC_info_server_t
*server_info
)
166 return (server_info
->inSync_NO
== 0) ? TRUE
: FALSE
;
171 * _libSC_info_server_open
173 * Called when a new configuration [XPC] connection
176 * - tracks the last generation pushed to the caller and
177 * the last generation ack'd by the caller
181 _libSC_info_server_open(libSC_info_server_t
*server_info
,
184 CFDataRef client_key
;
185 CFMutableDataRef client_val
;
188 client_key
= _client_key(c
);
190 client_val
= CFDataCreateMutable(NULL
, sizeof(*val
));
191 CFDataSetLength(client_val
, sizeof(*val
));
193 val
= (client_val_t
*)(void *)CFDataGetMutableBytePtr(client_val
);
194 val
->pid
= xpc_connection_get_pid(c
);
195 val
->generation_pushed
= 0;
196 val
->generation_acknowledged
= 0;
198 CFDictionarySetValue(server_info
->info
, client_key
, client_val
);
199 CFRelease(client_key
);
200 CFRelease(client_val
);
207 * _libSC_info_server_get_data
209 * Called when a [XPC] connection wants the current configuration.
211 * - updates the last generation pushed to the caller
215 _libSC_info_server_get_data(libSC_info_server_t
*server_info
,
217 uint64_t *generation
)
219 CFDataRef client_key
;
220 CFMutableDataRef client_val
;
223 // update last generation pushed to client
224 client_key
= _client_key(c
);
225 client_val
= (CFMutableDataRef
)CFDictionaryGetValue(server_info
->info
, client_key
);
226 CFRelease(client_key
);
228 val
= (client_val_t
*)(void *)CFDataGetMutableBytePtr(client_val
);
229 val
->generation_pushed
= server_info
->generation
;
232 *generation
= server_info
->generation
;
233 if (*generation
== 1) {
238 return server_info
->data
;
243 * _libSC_info_server_acknowledged
245 * Called when a [XPC] connection wants to acknowledge a
246 * processed configuration.
248 * - updates the last generation ack'd by the caller
249 * - updates the count of [XPC] connections that are / not in sync
253 _libSC_info_server_acknowledged(libSC_info_server_t
*server_info
,
257 CFDataRef client_key
;
258 CFMutableDataRef client_val
;
259 xpc_object_t ent_value
;
260 Boolean entitled
= FALSE
;
261 Boolean sync_updated
= FALSE
;
264 ent_value
= xpc_connection_copy_entitlement_value(c
, kTrailingEdgeAgentEntitlement
);
265 if (ent_value
!= NULL
) {
266 if (xpc_get_type(ent_value
) == XPC_TYPE_BOOL
) {
267 entitled
= xpc_bool_get_value(ent_value
);
269 xpc_release(ent_value
);
273 _handle_entitlement_check_failure(xpc_connection_get_pid(c
));
277 client_key
= _client_key(c
);
278 client_val
= (CFMutableDataRef
)CFDictionaryGetValue(server_info
->info
, client_key
);
279 CFRelease(client_key
);
281 val
= (client_val_t
*)(void *)CFDataGetMutableBytePtr(client_val
);
283 if (val
->generation_acknowledged
== 0) {
285 if (generation
== server_info
->generation
) {
286 server_info
->inSync_YES
++;
288 server_info
->inSync_NO
++;
291 } else if ((generation
!= val
->generation_acknowledged
) &&
292 (generation
== server_info
->generation
)) {
293 // if we've previously ack'd a configuration
294 // ... and if we are ack'ing a configuration
295 // that we have not previously ack'd
296 // ... and if we're ack'ing the current stored
298 server_info
->inSync_NO
--;
299 server_info
->inSync_YES
++;
303 val
->generation_acknowledged
= generation
;
310 * _libSC_info_server_close
312 * Called when a configuration [XPC] connection is closed.
316 _libSC_info_server_close(libSC_info_server_t
*server_info
,
319 CFDataRef client_key
;
320 CFMutableDataRef client_val
;
321 Boolean sync_updated
= FALSE
;
323 client_key
= _client_key(c
);
325 // get client info, remove ack'd info
326 client_val
= (CFMutableDataRef
)CFDictionaryGetValue(server_info
->info
, client_key
);
327 if (client_val
!= NULL
) {
330 val
= (client_val_t
*)(void *)CFDataGetMutableBytePtr(client_val
);
331 if (val
->generation_acknowledged
> 0) {
332 // if we've previously ack'd a configuration
333 if (val
->generation_acknowledged
== server_info
->generation
) {
334 // if currently in sync
335 server_info
->inSync_YES
--;
337 // if currently NOT in sync
338 server_info
->inSync_NO
--;
344 CFDictionaryRemoveValue(server_info
->info
, client_key
);
345 CFRelease(client_key
);