]> git.saurik.com Git - apple/configd.git/blob - libSystemConfiguration/libSystemConfiguration_server.c
configd-963.30.1.tar.gz
[apple/configd.git] / libSystemConfiguration / libSystemConfiguration_server.c
1 /*
2 * Copyright (c) 2012-2016 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 <dispatch/dispatch.h>
27 #include <vproc.h>
28 #include <vproc_priv.h>
29 #include <xpc/xpc.h>
30 #include <xpc/private.h>
31
32 #include <CoreFoundation/CoreFoundation.h>
33 #include <SystemConfiguration/SCPrivate.h>
34 #include "libSystemConfiguration_server.h"
35
36 #define kTrailingEdgeAgentEntitlement "com.apple.SystemConfiguration.trailing-edge-agent"
37
38 #ifdef SC_LOG_HANDLE
39 #include <os/log.h>
40 os_log_t SC_LOG_HANDLE;
41 #endif //SC_LOG_HANDLE
42
43
44 #pragma mark -
45 #pragma mark client connection trackng
46
47
48 typedef struct {
49 xpc_connection_t connection;
50 } client_key_t;
51
52 typedef struct {
53 pid_t pid;
54 uint64_t generation_pushed;
55 uint64_t generation_acknowledged;
56 } client_val_t;
57
58
59 static __inline__ CF_RETURNS_RETAINED CFDataRef
60 _client_key(xpc_connection_t c)
61 {
62 client_key_t key;
63 CFDataRef client_key;
64
65 key.connection = c;
66 client_key = CFDataCreate(NULL, (UInt8 *)&key, sizeof(key));
67 return client_key;
68 }
69
70
71 static void
72 _handle_entitlement_check_failure(pid_t pid)
73 {
74 static Boolean cleanupScheduled = FALSE;
75 static dispatch_once_t initializer = 0;
76 static CFMutableArrayRef pids = NULL;
77 static dispatch_queue_t queue = NULL;
78
79 dispatch_once(&initializer, ^{
80 pids = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
81 queue = dispatch_queue_create("handle unentitled ack", NULL);
82 });
83
84 dispatch_sync(queue, ^{
85 CFNumberRef pidNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid);
86
87 if (!CFArrayContainsValue(pids, CFRangeMake(0, CFArrayGetCount(pids)), pidNumber)) {
88 CFArrayAppendValue(pids, pidNumber);
89
90 SC_log(LOG_INFO, "DNS/nwi dropping ack w/no entitlement, pid = %d", pid);
91
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;
97 });
98 }
99 }
100
101 CFRelease(pidNumber);
102 });
103 }
104
105
106 /*
107 * libSystemConfiguraiton_client
108 *
109 * - all APIs must be called from the same [serial] dispatch queue
110 */
111
112
113 __private_extern__
114 void
115 _libSC_info_server_init(libSC_info_server_t *server_info) {
116 bzero(server_info, sizeof(*server_info));
117 server_info->info = CFDictionaryCreateMutable(NULL,
118 0,
119 &kCFTypeDictionaryKeyCallBacks,
120 &kCFTypeDictionaryValueCallBacks);
121 return;
122 }
123
124
125 __private_extern__
126 void
127 _libSC_info_server_set_data(libSC_info_server_t *server_info,
128 CFDataRef data,
129 uint64_t generation)
130 {
131 // update stored configuration
132 if (server_info->data != NULL) {
133 CFRelease(server_info->data);
134 server_info->data = NULL;
135 }
136 if (data != NULL) {
137 CFRetain(data);
138 server_info->data = data;
139 }
140
141 // update generation
142 if (generation == 0) {
143 // generation must be non-zero
144 generation = 1;
145 }
146 server_info->generation = generation;
147
148 // new configuration, all ack'ing clients need to
149 // check-in again
150 server_info->inSync_NO += server_info->inSync_YES;
151 server_info->inSync_YES = 0;
152
153 return;
154 }
155
156
157 /*
158 * _libSC_info_server_in_sync
159 *
160 * Called to check if all of the "active" configuration [XPC] connection
161 * are in sync with the requested generation.
162 */
163 __private_extern__
164 Boolean
165 _libSC_info_server_in_sync(libSC_info_server_t *server_info)
166 {
167 return (server_info->inSync_NO == 0) ? TRUE : FALSE;
168 }
169
170
171 /*
172 * _libSC_info_server_open
173 *
174 * Called when a new configuration [XPC] connection
175 * is established.
176 *
177 * - tracks the last generation pushed to the caller and
178 * the last generation ack'd by the caller
179 */
180 __private_extern__
181 void
182 _libSC_info_server_open(libSC_info_server_t *server_info,
183 xpc_connection_t c)
184 {
185 CFDataRef client_key;
186 CFMutableDataRef client_val;
187 client_val_t *val;
188
189 client_key = _client_key(c);
190
191 client_val = CFDataCreateMutable(NULL, sizeof(*val));
192 CFDataSetLength(client_val, sizeof(*val));
193
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;
198
199 CFDictionarySetValue(server_info->info, client_key, client_val);
200 CFRelease(client_key);
201 CFRelease(client_val);
202
203 return;
204 }
205
206
207 /*
208 * _libSC_info_server_get_data
209 *
210 * Called when a [XPC] connection wants the current configuration.
211 *
212 * - updates the last generation pushed to the caller
213 */
214 __private_extern__
215 CFDataRef
216 _libSC_info_server_get_data(libSC_info_server_t *server_info,
217 xpc_connection_t c,
218 uint64_t *generation)
219 {
220 CFDataRef client_key;
221 CFMutableDataRef client_val;
222 client_val_t *val;
223
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);
228
229 val = (client_val_t *)(void *)CFDataGetMutableBytePtr(client_val);
230 val->generation_pushed = server_info->generation;
231
232 // return generation
233 *generation = server_info->generation;
234 if (*generation == 1) {
235 *generation = 0;
236 }
237
238 // return data
239 return server_info->data;
240 }
241
242
243 /*
244 * _libSC_info_server_acknowledged
245 *
246 * Called when a [XPC] connection wants to acknowledge a
247 * processed configuration.
248 *
249 * - updates the last generation ack'd by the caller
250 * - updates the count of [XPC] connections that are / not in sync
251 */
252 __private_extern__
253 Boolean
254 _libSC_info_server_acknowledged(libSC_info_server_t *server_info,
255 xpc_connection_t c,
256 uint64_t generation)
257 {
258 CFDataRef client_key;
259 CFMutableDataRef client_val;
260 xpc_object_t ent_value;
261 Boolean entitled = FALSE;
262 Boolean sync_updated = FALSE;
263 client_val_t *val;
264
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);
269 }
270 xpc_release(ent_value);
271 }
272
273 if (!entitled) {
274 _handle_entitlement_check_failure(xpc_connection_get_pid(c));
275 return FALSE;
276 }
277
278 client_key = _client_key(c);
279 client_val = (CFMutableDataRef)CFDictionaryGetValue(server_info->info, client_key);
280 CFRelease(client_key);
281
282 val = (client_val_t *)(void *)CFDataGetMutableBytePtr(client_val);
283
284 if (val->generation_acknowledged == 0) {
285 // if first ack
286 if (generation == server_info->generation) {
287 server_info->inSync_YES++;
288 } else {
289 server_info->inSync_NO++;
290 sync_updated = TRUE;
291 }
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
298 // configuration
299 server_info->inSync_NO--;
300 server_info->inSync_YES++;
301 sync_updated = TRUE;
302 }
303
304 val->generation_acknowledged = generation;
305
306 return sync_updated;
307 }
308
309
310 /*
311 * _libSC_info_server_close
312 *
313 * Called when a configuration [XPC] connection is closed.
314 */
315 __private_extern__
316 Boolean
317 _libSC_info_server_close(libSC_info_server_t *server_info,
318 xpc_connection_t c)
319 {
320 CFDataRef client_key;
321 CFMutableDataRef client_val;
322 Boolean sync_updated = FALSE;
323
324 client_key = _client_key(c);
325
326 // get client info, remove ack'd info
327 client_val = (CFMutableDataRef)CFDictionaryGetValue(server_info->info, client_key);
328 if (client_val != NULL) {
329 client_val_t *val;
330
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--;
337 } else {
338 // if currently NOT in sync
339 server_info->inSync_NO--;
340 sync_updated = TRUE;
341 }
342 }
343 }
344
345 CFDictionaryRemoveValue(server_info->info, client_key);
346 CFRelease(client_key);
347
348 return sync_updated;
349 }
350