]> git.saurik.com Git - apple/configd.git/blob - libSystemConfiguration/libSystemConfiguration_server.c
82a201ad4b4da8ea784bc79598671e7f2957a5ce
[apple/configd.git] / libSystemConfiguration / libSystemConfiguration_server.c
1 /*
2 * Copyright (c) 2012, 2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <Availability.h>
25 #include <TargetConditionals.h>
26 #include <asl.h>
27 #include <dispatch/dispatch.h>
28 #include <vproc.h>
29 #include <vproc_priv.h>
30 #include <xpc/xpc.h>
31 #include <xpc/private.h>
32
33 #include <CoreFoundation/CoreFoundation.h>
34 #include <SystemConfiguration/SCPrivate.h>
35
36 #include "libSystemConfiguration_server.h"
37
38 #define kTrailingEdgeAgentEntitlement "com.apple.SystemConfiguration.trailing-edge-agent"
39
40 #pragma mark -
41 #pragma mark Support functions
42
43
44 //__private_extern__ void
45 //log_xpc_object(const char *msg, xpc_object_t obj)
46 //{
47 // char *desc;
48 //
49 // desc = xpc_copy_description(obj);
50 // asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s = %s", msg, desc);
51 // free(desc);
52 //}
53
54
55 #pragma mark -
56 #pragma mark client connection trackng
57
58
59 typedef struct {
60 xpc_connection_t connection;
61 } client_key_t;
62
63 typedef struct {
64 pid_t pid;
65 uint64_t generation_pushed;
66 uint64_t generation_acknowledged;
67 } client_val_t;
68
69
70 static __inline__ CF_RETURNS_RETAINED CFDataRef
71 _client_key(xpc_connection_t c)
72 {
73 client_key_t key;
74 CFDataRef client_key;
75
76 key.connection = c;
77 client_key = CFDataCreate(NULL, (UInt8 *)&key, sizeof(key));
78 return client_key;
79 }
80
81
82 #if ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
83 static void
84 _handle_entitlement_check_failure(pid_t pid)
85 {
86 static Boolean cleanupScheduled = FALSE;
87 static dispatch_once_t initializer = 0;
88 static CFMutableArrayRef pids = NULL;
89 static dispatch_queue_t queue = NULL;
90
91 dispatch_once(&initializer, ^{
92 pids = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
93 queue = dispatch_queue_create("handle unentitled ack", NULL);
94 });
95
96 dispatch_sync(queue, ^{
97 CFNumberRef pidNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid);
98
99 if (!CFArrayContainsValue(pids, CFRangeMake(0, CFArrayGetCount(pids)), pidNumber)) {
100 CFArrayAppendValue(pids, pidNumber);
101
102 SCLog(TRUE, LOG_ERR, CFSTR("DNS/nwi dropping ack w/no entitlement, pid = %d"), pid);
103
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;
109 });
110 }
111 }
112
113 CFRelease(pidNumber);
114 });
115 }
116 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
117
118
119 /*
120 * libSystemConfiguraiton_client
121 *
122 * - all APIs must be called from the same [serial] dispatch queue
123 */
124
125
126 __private_extern__
127 void
128 _libSC_info_server_init(libSC_info_server_t *server_info) {
129 bzero(server_info, sizeof(*server_info));
130 server_info->info = CFDictionaryCreateMutable(NULL,
131 0,
132 &kCFTypeDictionaryKeyCallBacks,
133 &kCFTypeDictionaryValueCallBacks);
134 return;
135 }
136
137
138 __private_extern__
139 void
140 _libSC_info_server_set_data(libSC_info_server_t *server_info,
141 CFDataRef data,
142 uint64_t generation)
143 {
144 // update stored configuration
145 if (server_info->data != NULL) {
146 CFRelease(server_info->data);
147 server_info->data = NULL;
148 }
149 if (data != NULL) {
150 CFRetain(data);
151 server_info->data = data;
152 }
153
154 // update generation
155 if (generation == 0) {
156 // generation must be non-zero
157 generation = 1;
158 }
159 server_info->generation = generation;
160
161 // new configuration, all ack'ing clients need to
162 // check-in again
163 server_info->inSync_NO += server_info->inSync_YES;
164 server_info->inSync_YES = 0;
165
166 return;
167 }
168
169
170 /*
171 * _libSC_info_server_in_sync
172 *
173 * Called to check if all of the "active" configuration [XPC] connection
174 * are in sync with the requested generation.
175 */
176 __private_extern__
177 Boolean
178 _libSC_info_server_in_sync(libSC_info_server_t *server_info)
179 {
180 return (server_info->inSync_NO == 0) ? TRUE : FALSE;
181 }
182
183
184 /*
185 * _libSC_info_server_open
186 *
187 * Called when a new configuration [XPC] connection
188 * is established.
189 *
190 * - tracks the last generation pushed to the caller and
191 * the last generation ack'd by the caller
192 */
193 __private_extern__
194 void
195 _libSC_info_server_open(libSC_info_server_t *server_info,
196 xpc_connection_t c)
197 {
198 CFDataRef client_key;
199 CFDataRef client_val;
200 client_val_t val;
201
202 client_key = _client_key(c);
203
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));
208
209 CFDictionarySetValue(server_info->info, client_key, client_val);
210 CFRelease(client_key);
211 CFRelease(client_val);
212
213 return;
214 }
215
216
217 /*
218 * _libSC_info_server_get_data
219 *
220 * Called when a [XPC] connection wants the current configuration.
221 *
222 * - updates the last generation pushed to the caller
223 */
224 __private_extern__
225 CFDataRef
226 _libSC_info_server_get_data(libSC_info_server_t *server_info,
227 xpc_connection_t c,
228 uint64_t *generation)
229 {
230 CFDataRef client_key;
231 CFDataRef client_val;
232 client_val_t *val;
233
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);
238
239 val = (client_val_t *)(void *)CFDataGetBytePtr(client_val);
240 val->generation_pushed = server_info->generation;
241
242 // return generation
243 *generation = server_info->generation;
244 if (*generation == 1) {
245 *generation = 0;
246 }
247
248 // return data
249 return server_info->data;
250 }
251
252
253 /*
254 * _libSC_info_server_acknowledged
255 *
256 * Called when a [XPC] connection wants to acknowledge a
257 * processed configuration.
258 *
259 * - updates the last generation ack'd by the caller
260 * - updates the count of [XPC] connections that are / not in sync
261 */
262 __private_extern__
263 Boolean
264 _libSC_info_server_acknowledged(libSC_info_server_t *server_info,
265 xpc_connection_t c,
266 uint64_t generation)
267 {
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;
275 client_val_t *val;
276
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);
282 }
283 xpc_release(ent_value);
284 }
285
286 if (!entitled) {
287 _handle_entitlement_check_failure(xpc_connection_get_pid(c));
288 return FALSE;
289 }
290 #endif // ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
291
292 client_key = _client_key(c);
293 client_val = CFDictionaryGetValue(server_info->info, client_key);
294 CFRelease(client_key);
295
296 val = (client_val_t *)(void *)CFDataGetBytePtr(client_val);
297
298 if (val->generation_acknowledged == 0) {
299 // if first ack
300 if (generation == server_info->generation) {
301 server_info->inSync_YES++;
302 } else {
303 server_info->inSync_NO++;
304 sync_updated = TRUE;
305 }
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
312 // configuration
313 server_info->inSync_NO--;
314 server_info->inSync_YES++;
315 sync_updated = TRUE;
316 }
317
318 val->generation_acknowledged = generation;
319
320 return sync_updated;
321 }
322
323
324 /*
325 * _libSC_info_server_close
326 *
327 * Called when a configuration [XPC] connection is closed.
328 */
329 __private_extern__
330 Boolean
331 _libSC_info_server_close(libSC_info_server_t *server_info,
332 xpc_connection_t c)
333 {
334 CFDataRef client_key;
335 CFDataRef client_val;
336 Boolean sync_updated = FALSE;
337
338 client_key = _client_key(c);
339
340 // get client info, remove ack'd info
341 client_val = CFDictionaryGetValue(server_info->info, client_key);
342 if (client_val != NULL) {
343 client_val_t *val;
344
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--;
351 } else {
352 // if currently NOT in sync
353 server_info->inSync_NO--;
354 sync_updated = TRUE;
355 }
356 }
357 }
358
359 CFDictionaryRemoveValue(server_info->info, client_key);
360 CFRelease(client_key);
361
362 return sync_updated;
363 }
364