]> git.saurik.com Git - apple/configd.git/blob - nwi/network_information_server.c
configd-802.40.13.tar.gz
[apple/configd.git] / nwi / network_information_server.c
1 /*
2 * Copyright (c) 2012-2015 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 /*
25 * Modification History
26 *
27 * February 8, 2012 Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31 #include <notify.h>
32 #include <dispatch/dispatch.h>
33 #include <xpc/xpc.h>
34 #include <CommonCrypto/CommonDigest.h>
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <SystemConfiguration/SCPrivate.h>
37
38 #include "libSystemConfiguration_client.h"
39 #include "libSystemConfiguration_server.h"
40
41 #include <network_information.h>
42 #include "network_information_priv.h"
43 #include "network_information_server.h"
44
45
46 #pragma mark -
47 #pragma mark Globals
48
49
50 /*
51 * S_nwi_info
52 *
53 * Note: all accesses should be made while running on the _nwi_server_queue()
54 */
55 static libSC_info_server_t S_nwi_info;
56
57
58 /*
59 * S_logger
60 * Logging handle.
61 */
62 static SCLoggerRef S_logger = NULL;
63
64
65 /*
66 * S_sync_handler
67 * ACK (in-sync or not-in-sync) updates should be posted using
68 * this handler
69 *
70 * Note: all accesses should be made while running on the _nwi_server_queue()
71 */
72 static _nwi_sync_handler_t S_sync_handler = NULL;
73
74
75 #pragma mark -
76 #pragma mark Support functions
77
78
79 #pragma mark -
80 #pragma mark Network information server "main"
81
82
83 static dispatch_queue_t
84 _nwi_state_server_queue()
85 {
86 static dispatch_once_t once;
87 static dispatch_queue_t q;
88
89 dispatch_once(&once, ^{
90 q = dispatch_queue_create(NWI_SERVICE_NAME ".server", NULL);
91 });
92
93 return q;
94 }
95
96
97 /*
98 * _nwi_state_copy
99 *
100 * Called when a client wants a copy of the current
101 * Network information
102 *
103 * - caller must be running on the _nwi_server_queue()
104 */
105 static void
106 _nwi_state_copy(xpc_connection_t connection, xpc_object_t request)
107 {
108 CFDataRef data;
109 uint64_t generation;
110 const char *proc_name;
111 xpc_connection_t remote;
112 xpc_object_t reply;
113
114 remote = xpc_dictionary_get_remote_connection(request);
115 reply = xpc_dictionary_create_reply(request);
116 if (reply == NULL) {
117 SCLoggerLog(S_logger, LOG_ERR,
118 CFSTR("<%p> _nwi_state_copy: xpc_dictionary_create_reply: failed"),
119 connection);
120 return;
121 }
122
123 // extract data and generation #
124 data = _libSC_info_server_get_data(&S_nwi_info, connection, &generation);
125
126 // extract process name
127 proc_name = xpc_dictionary_get_string(request, NWI_PROC_NAME);
128 if (proc_name == NULL) {
129 proc_name = "???";
130 }
131
132 SCLoggerLog(S_logger, LOG_DEBUG, CFSTR("<%p:%s[%d]> Network information copy: %llu"),
133 connection,
134 proc_name,
135 xpc_connection_get_pid(connection),
136 generation);
137
138 // return the Network information (if available)
139 if (data != NULL) {
140 xpc_dictionary_set_data(reply,
141 NWI_CONFIGURATION,
142 CFDataGetBytePtr(data),
143 CFDataGetLength(data));
144 }
145
146 // reply
147 xpc_connection_send_message(remote, reply);
148 xpc_release(reply);
149
150 return;
151 }
152
153
154 /*
155 * _nwi_state_acknowledge
156 *
157 * Called when a client wants to acknowledge processing
158 * of the Network information
159 *
160 * - caller must be running on the _nwi_server_queue()
161 */
162 static void
163 _nwi_state_acknowledge(xpc_connection_t connection, xpc_object_t request)
164 {
165 Boolean changed;
166 uint64_t generation;
167
168 generation = xpc_dictionary_get_uint64(request, NWI_GENERATION);
169
170 SCLoggerLog(S_logger, LOG_DEBUG, CFSTR("<%p:%d> Network information ack: %llu"),
171 connection,
172 xpc_connection_get_pid(connection),
173 generation);
174
175 _libSC_info_server_acknowledged(&S_nwi_info, connection, generation);
176 changed = _libSC_info_server_acknowledged(&S_nwi_info, connection, generation);
177 if (changed) {
178 Boolean inSync;
179
180 // report change
181 inSync = _libSC_info_server_in_sync(&S_nwi_info);
182 S_sync_handler(inSync);
183 }
184
185 return;
186 }
187
188
189 static void
190 process_request(xpc_connection_t connection, xpc_object_t request)
191 {
192 int64_t op;
193
194 op = xpc_dictionary_get_int64(request, NWI_REQUEST);
195 switch (op) {
196 case NWI_REQUEST_COPY :
197 /*
198 * Return the Network information
199 */
200 _nwi_state_copy(connection, request);
201 break;
202
203 case NWI_REQUEST_ACKNOWLEDGE :
204 /*
205 * Acknowlege a [processed] Network information
206 */
207 _nwi_state_acknowledge(connection, request);
208
209 break;
210 default :
211 SCLoggerLog(S_logger, LOG_ERR,
212 CFSTR("<%p> unknown request : %lld"),
213 connection,
214 op);
215
216 break;
217 }
218
219 return;
220 }
221
222
223 static void
224 process_new_connection(xpc_connection_t c)
225 {
226 SCLoggerLog(S_logger, LOG_DEBUG, CFSTR("<%p:%d> Network information session: open"),
227 c,
228 xpc_connection_get_pid(c));
229
230 _libSC_info_server_open(&S_nwi_info, c);
231
232 xpc_connection_set_target_queue(c, _nwi_state_server_queue());
233
234 xpc_connection_set_event_handler(c, ^(xpc_object_t xobj) {
235 os_activity_t activity_id;
236 xpc_type_t type;
237
238 activity_id = os_activity_start("processing nwi request",
239 OS_ACTIVITY_FLAG_DEFAULT);
240
241 type = xpc_get_type(xobj);
242 if (type == XPC_TYPE_DICTIONARY) {
243 // process the request
244 process_request(c, xobj);
245
246 } else if (type == XPC_TYPE_ERROR) {
247 const char *desc;
248
249 desc = xpc_dictionary_get_string(xobj, XPC_ERROR_KEY_DESCRIPTION);
250 if (xobj == XPC_ERROR_CONNECTION_INVALID) {
251 Boolean changed;
252
253 SCLoggerLog(S_logger, LOG_DEBUG, CFSTR("<%p:%d> Network information session: close"),
254 c,
255 xpc_connection_get_pid(c));
256
257 changed = _libSC_info_server_close(&S_nwi_info, c);
258 if (changed) {
259 Boolean inSync;
260
261 // report change
262 inSync = _libSC_info_server_in_sync(&S_nwi_info);
263 S_sync_handler(inSync);
264 }
265
266 } else if (xobj == XPC_ERROR_CONNECTION_INTERRUPTED) {
267 SCLoggerLog(S_logger, LOG_ERR,
268 CFSTR("<%p:%d> %s"),
269 c,
270 xpc_connection_get_pid(c),
271 desc);
272
273 } else {
274 SCLoggerLog(S_logger, LOG_ERR,
275 CFSTR("<%p:%d> Connection error: %p : %s"),
276 c,
277 xpc_connection_get_pid(c),
278 xobj,
279 desc);
280 }
281
282 } else {
283 SCLoggerLog(S_logger, LOG_ERR,
284 CFSTR("<%p:%d> unknown event type : %p"),
285 c,
286 xpc_connection_get_pid(c),
287 type);
288 }
289
290 os_activity_end(activity_id);
291 });
292
293 xpc_connection_resume(c);
294
295 return;
296 }
297
298
299 #pragma mark -
300 #pragma mark Network Information server SPIs
301
302
303 __private_extern__
304 void
305 load_NetworkInformation(CFBundleRef bundle,
306 SCLoggerRef logger,
307 _nwi_sync_handler_t syncHandler)
308 {
309 xpc_connection_t c;
310 const char *name;
311
312 S_logger = logger;
313
314 /*
315 * keep track of Network information acknowledgements
316 */
317 _libSC_info_server_init(&S_nwi_info);
318
319 /*
320 * save the in-sync/not-in-sync handler
321 */
322 S_sync_handler = Block_copy(syncHandler);
323
324 // create XPC listener
325 name = getenv(NWI_SERVICE_NAME);
326 if (name == NULL) {
327 name = NWI_SERVICE_NAME;
328 }
329
330 c = xpc_connection_create_mach_service(name,
331 _nwi_state_server_queue(),
332 XPC_CONNECTION_MACH_SERVICE_LISTENER);
333
334 xpc_connection_set_event_handler(c, ^(xpc_object_t event) {
335 os_activity_t activity_id;
336 xpc_type_t type;
337
338 activity_id = os_activity_start("processing nwi connection",
339 OS_ACTIVITY_FLAG_DEFAULT);
340
341 type = xpc_get_type(event);
342 if (type == XPC_TYPE_CONNECTION) {
343 process_new_connection(event);
344
345 } else if (type == XPC_TYPE_ERROR) {
346 const char *desc;
347
348 desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
349 if (event == XPC_ERROR_CONNECTION_INVALID) {
350 SCLoggerLog(S_logger, LOG_ERR, CFSTR("Network information server: %s"), desc);
351 xpc_release(c);
352 } else if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
353 SCLoggerLog(S_logger, LOG_ERR, CFSTR("Network information server: %s"), desc);
354 } else {
355 SCLoggerLog(S_logger, LOG_ERR,
356 CFSTR("Network information server: Connection error: %p : %s"),
357 event,
358 desc);
359 }
360
361 } else {
362 SCLoggerLog(S_logger, LOG_ERR,
363 CFSTR("Network information server: unknown event type : %p"),
364 type);
365
366 }
367
368 os_activity_end(activity_id);
369 });
370
371 xpc_connection_resume(c);
372
373 SCLoggerLog(S_logger, LOG_DEBUG, CFSTR("XPC server \"%s\" started"), name);
374
375 return;
376 }
377
378
379 __private_extern__
380 _Bool
381 _nwi_state_store(nwi_state *state)
382 {
383 Boolean in_sync;
384 uint64_t new_generation = 0;
385 CFDataRef new_nwi_info = NULL;
386 const char *notify_key;
387
388 if (state != NULL) {
389 const UInt8 *bytes;
390 CFIndex len;
391
392 new_generation = state->generation_count;
393
394 SCLoggerLog(S_logger, LOG_DEBUG, CFSTR("Network information updated: %llu"),
395 new_generation);
396
397 bytes = (const UInt8 *)state;
398 len = nwi_state_size(state);
399
400 new_nwi_info = CFDataCreate(NULL, bytes, len);
401 }
402
403 dispatch_sync(_nwi_state_server_queue(), ^{
404 _libSC_info_server_set_data(&S_nwi_info, new_nwi_info, new_generation);
405 });
406
407 if (new_nwi_info != NULL) {
408 CFRelease(new_nwi_info);
409 }
410
411 // if anyone is keeping us in sync, they now need to catchup
412 in_sync = _libSC_info_server_in_sync(&S_nwi_info);
413 S_sync_handler(in_sync);
414
415 // and let everyone else know that the configuration has been updated
416 notify_key = nwi_state_get_notify_key();
417 if (notify_key != NULL) {
418 uint32_t status;
419
420 _nwi_state_force_refresh();
421 status = notify_post(notify_key);
422 if (status != NOTIFY_STATUS_OK) {
423 SCLoggerLog(S_logger, LOG_ERR, CFSTR("notify_post() failed: %d"), status);
424 // notification posting failures are non-fatal
425 }
426 }
427
428 return TRUE;
429 }
430
431
432 #pragma mark -
433 #pragma mark Testing
434
435
436 #ifdef MAIN
437
438 int
439 main(int argc, char **argv)
440 {
441 static Boolean verbose = (argc > 1) ? TRUE : FALSE;
442 // _sc_log = FALSE;
443 _sc_verbose = (argc > 1) ? TRUE : FALSE;
444 _sc_debug = TRUE;
445
446 load_NetworkInformation(CFBundleGetMainBundle(), // bundle
447 NULL, // SCLogger
448 ^(Boolean inSync) { // sync handler
449 SCLoggerLog(NULL, LOG_INFO,
450 CFSTR("in sync: %s"),
451 inSync ? "Yes" : "No");
452 });
453 CFRunLoopRun();
454 /* not reached */
455 exit(0);
456 return 0;
457 }
458
459 #endif /* MAIN */