2 * Copyright (c) 2012-2015 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@
25 * Modification History
27 * February 8, 2012 Allan Nathanson <ajn@apple.com>
32 #include <dispatch/dispatch.h>
34 #include <CommonCrypto/CommonDigest.h>
35 #include <CoreFoundation/CoreFoundation.h>
36 #include <SystemConfiguration/SCPrivate.h>
38 #include "libSystemConfiguration_client.h"
39 #include "libSystemConfiguration_server.h"
41 #include <network_information.h>
42 #include "network_information_priv.h"
43 #include "network_information_server.h"
53 * Note: all accesses should be made while running on the _nwi_server_queue()
55 static libSC_info_server_t S_nwi_info
;
62 static SCLoggerRef S_logger
= NULL
;
67 * ACK (in-sync or not-in-sync) updates should be posted using
70 * Note: all accesses should be made while running on the _nwi_server_queue()
72 static _nwi_sync_handler_t S_sync_handler
= NULL
;
76 #pragma mark Support functions
80 #pragma mark Network information server "main"
83 static dispatch_queue_t
84 _nwi_state_server_queue()
86 static dispatch_once_t once
;
87 static dispatch_queue_t q
;
89 dispatch_once(&once
, ^{
90 q
= dispatch_queue_create(NWI_SERVICE_NAME
".server", NULL
);
100 * Called when a client wants a copy of the current
101 * Network information
103 * - caller must be running on the _nwi_server_queue()
106 _nwi_state_copy(xpc_connection_t connection
, xpc_object_t request
)
110 const char *proc_name
;
111 xpc_connection_t remote
;
114 remote
= xpc_dictionary_get_remote_connection(request
);
115 reply
= xpc_dictionary_create_reply(request
);
117 SCLoggerLog(S_logger
, LOG_ERR
,
118 CFSTR("<%p> _nwi_state_copy: xpc_dictionary_create_reply: failed"),
123 // extract data and generation #
124 data
= _libSC_info_server_get_data(&S_nwi_info
, connection
, &generation
);
126 // extract process name
127 proc_name
= xpc_dictionary_get_string(request
, NWI_PROC_NAME
);
128 if (proc_name
== NULL
) {
132 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("<%p:%s[%d]> Network information copy: %llu"),
135 xpc_connection_get_pid(connection
),
138 // return the Network information (if available)
140 xpc_dictionary_set_data(reply
,
142 CFDataGetBytePtr(data
),
143 CFDataGetLength(data
));
147 xpc_connection_send_message(remote
, reply
);
155 * _nwi_state_acknowledge
157 * Called when a client wants to acknowledge processing
158 * of the Network information
160 * - caller must be running on the _nwi_server_queue()
163 _nwi_state_acknowledge(xpc_connection_t connection
, xpc_object_t request
)
168 generation
= xpc_dictionary_get_uint64(request
, NWI_GENERATION
);
170 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("<%p:%d> Network information ack: %llu"),
172 xpc_connection_get_pid(connection
),
175 _libSC_info_server_acknowledged(&S_nwi_info
, connection
, generation
);
176 changed
= _libSC_info_server_acknowledged(&S_nwi_info
, connection
, generation
);
181 inSync
= _libSC_info_server_in_sync(&S_nwi_info
);
182 S_sync_handler(inSync
);
190 process_request(xpc_connection_t connection
, xpc_object_t request
)
194 op
= xpc_dictionary_get_int64(request
, NWI_REQUEST
);
196 case NWI_REQUEST_COPY
:
198 * Return the Network information
200 _nwi_state_copy(connection
, request
);
203 case NWI_REQUEST_ACKNOWLEDGE
:
205 * Acknowlege a [processed] Network information
207 _nwi_state_acknowledge(connection
, request
);
211 SCLoggerLog(S_logger
, LOG_ERR
,
212 CFSTR("<%p> unknown request : %lld"),
224 process_new_connection(xpc_connection_t c
)
226 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("<%p:%d> Network information session: open"),
228 xpc_connection_get_pid(c
));
230 _libSC_info_server_open(&S_nwi_info
, c
);
232 xpc_connection_set_target_queue(c
, _nwi_state_server_queue());
234 xpc_connection_set_event_handler(c
, ^(xpc_object_t xobj
) {
235 os_activity_t activity_id
;
238 activity_id
= os_activity_start("processing nwi request",
239 OS_ACTIVITY_FLAG_DEFAULT
);
241 type
= xpc_get_type(xobj
);
242 if (type
== XPC_TYPE_DICTIONARY
) {
243 // process the request
244 process_request(c
, xobj
);
246 } else if (type
== XPC_TYPE_ERROR
) {
249 desc
= xpc_dictionary_get_string(xobj
, XPC_ERROR_KEY_DESCRIPTION
);
250 if (xobj
== XPC_ERROR_CONNECTION_INVALID
) {
253 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("<%p:%d> Network information session: close"),
255 xpc_connection_get_pid(c
));
257 changed
= _libSC_info_server_close(&S_nwi_info
, c
);
262 inSync
= _libSC_info_server_in_sync(&S_nwi_info
);
263 S_sync_handler(inSync
);
266 } else if (xobj
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
267 SCLoggerLog(S_logger
, LOG_ERR
,
270 xpc_connection_get_pid(c
),
274 SCLoggerLog(S_logger
, LOG_ERR
,
275 CFSTR("<%p:%d> Connection error: %p : %s"),
277 xpc_connection_get_pid(c
),
283 SCLoggerLog(S_logger
, LOG_ERR
,
284 CFSTR("<%p:%d> unknown event type : %p"),
286 xpc_connection_get_pid(c
),
290 os_activity_end(activity_id
);
293 xpc_connection_resume(c
);
300 #pragma mark Network Information server SPIs
305 load_NetworkInformation(CFBundleRef bundle
,
307 _nwi_sync_handler_t syncHandler
)
315 * keep track of Network information acknowledgements
317 _libSC_info_server_init(&S_nwi_info
);
320 * save the in-sync/not-in-sync handler
322 S_sync_handler
= Block_copy(syncHandler
);
324 // create XPC listener
325 name
= getenv(NWI_SERVICE_NAME
);
327 name
= NWI_SERVICE_NAME
;
330 c
= xpc_connection_create_mach_service(name
,
331 _nwi_state_server_queue(),
332 XPC_CONNECTION_MACH_SERVICE_LISTENER
);
334 xpc_connection_set_event_handler(c
, ^(xpc_object_t event
) {
335 os_activity_t activity_id
;
338 activity_id
= os_activity_start("processing nwi connection",
339 OS_ACTIVITY_FLAG_DEFAULT
);
341 type
= xpc_get_type(event
);
342 if (type
== XPC_TYPE_CONNECTION
) {
343 process_new_connection(event
);
345 } else if (type
== XPC_TYPE_ERROR
) {
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
);
352 } else if (event
== XPC_ERROR_CONNECTION_INTERRUPTED
) {
353 SCLoggerLog(S_logger
, LOG_ERR
, CFSTR("Network information server: %s"), desc
);
355 SCLoggerLog(S_logger
, LOG_ERR
,
356 CFSTR("Network information server: Connection error: %p : %s"),
362 SCLoggerLog(S_logger
, LOG_ERR
,
363 CFSTR("Network information server: unknown event type : %p"),
368 os_activity_end(activity_id
);
371 xpc_connection_resume(c
);
373 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("XPC server \"%s\" started"), name
);
381 _nwi_state_store(nwi_state
*state
)
384 uint64_t new_generation
= 0;
385 CFDataRef new_nwi_info
= NULL
;
386 const char *notify_key
;
392 new_generation
= state
->generation_count
;
394 SCLoggerLog(S_logger
, LOG_DEBUG
, CFSTR("Network information updated: %llu"),
397 bytes
= (const UInt8
*)state
;
398 len
= nwi_state_size(state
);
400 new_nwi_info
= CFDataCreate(NULL
, bytes
, len
);
403 dispatch_sync(_nwi_state_server_queue(), ^{
404 _libSC_info_server_set_data(&S_nwi_info
, new_nwi_info
, new_generation
);
407 if (new_nwi_info
!= NULL
) {
408 CFRelease(new_nwi_info
);
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
);
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
) {
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
439 main(int argc
, char **argv
)
441 static Boolean verbose
= (argc
> 1) ? TRUE
: FALSE
;
443 _sc_verbose
= (argc
> 1) ? TRUE
: FALSE
;
446 load_NetworkInformation(CFBundleGetMainBundle(), // bundle
448 ^(Boolean inSync
) { // sync handler
449 SCLoggerLog(NULL
, LOG_INFO
,
450 CFSTR("in sync: %s"),
451 inSync
? "Yes" : "No");