/* * Copyright (c) 2013-2018 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * IPMonitorControlServer.c * - IPC channel to IPMonitor * - used to create interface rank assertions */ /* * Modification History * * December 16, 2013 Dieter Siegmund (dieter@apple.com) * - initial revision */ #include #include #include #include #include #include #include #include "IPMonitorControlServer.h" #include "symbol_scope.h" #include "IPMonitorControlPrivate.h" #include "IPMonitorAWDReport.h" #ifdef TEST_IPMONITOR_CONTROL #define my_log(__level, __format, ...) SCPrint(TRUE, stdout, CFSTR(__format "\n"), ## __VA_ARGS__) #else /* TEST_IPMONITOR_CONTROL */ #include "ip_plugin.h" #endif /* TEST_IPMONITOR_CONTROL */ STATIC dispatch_queue_t S_IPMonitorControlServerQueue; typedef struct ControlSession ControlSession, * ControlSessionRef; #define LIST_HEAD_ControlSession LIST_HEAD(ControlSessionHead, ControlSession) #define LIST_ENTRY_ControlSession LIST_ENTRY(ControlSession) LIST_HEAD_ControlSession S_ControlSessions; struct ControlSession { LIST_ENTRY_ControlSession link; xpc_connection_t connection; CFMutableDictionaryRef assertions; /* ifname = rank */ CFMutableDictionaryRef advisories; /* ifname = advisory */ }; /** ** Support Functions **/ STATIC CFMutableArrayRef S_if_changes; STATIC CFRange S_if_changes_range; STATIC CFNumberRef RankLastNumberGet(void) { STATIC CFNumberRef rank_last; if (rank_last == NULL) { SCNetworkServicePrimaryRank rank; rank = kSCNetworkServicePrimaryRankLast; rank_last = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank); } return (rank_last); } STATIC void InterfaceChangedListAddInterface(CFStringRef ifname) { if (S_if_changes == NULL) { S_if_changes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); CFArrayAppendValue(S_if_changes, ifname); S_if_changes_range.length = 1; } else if (!CFArrayContainsValue(S_if_changes, S_if_changes_range, ifname)) { CFArrayAppendValue(S_if_changes, ifname); S_if_changes_range.length++; } } STATIC CFArrayRef InterfaceChangedListCopy(void) { CFArrayRef current_list; current_list = S_if_changes; S_if_changes = NULL; return (current_list); } STATIC void InterfaceRankAssertionAdd(const void * key, const void * value, void * context) { CFMutableDictionaryRef * assertions_p; CFNumberRef existing_rank; CFNumberRef rank = (CFNumberRef)value; assertions_p = (CFMutableDictionaryRef *)context; if (*assertions_p == NULL) { *assertions_p = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(*assertions_p, key, rank); return; } existing_rank = CFDictionaryGetValue(*assertions_p, key); if (existing_rank == NULL || (CFNumberCompare(rank, existing_rank, NULL) == kCFCompareGreaterThan)) { CFDictionarySetValue(*assertions_p, key, rank); } return; } STATIC void InterfaceAdvisoryAdd(const void * key, const void * value, void * context) { #pragma unused(value) CFMutableDictionaryRef * assertions_p; CFNumberRef existing_rank; CFNumberRef rank; /* an interface advisory implies RankLast */ rank = RankLastNumberGet(); assertions_p = (CFMutableDictionaryRef *)context; if (*assertions_p == NULL) { *assertions_p = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(*assertions_p, key, rank); return; } existing_rank = CFDictionaryGetValue(*assertions_p, key); if (existing_rank == NULL || (CFNumberCompare(rank, existing_rank, NULL) == kCFCompareGreaterThan)) { CFDictionarySetValue(*assertions_p, key, rank); } return; } STATIC CFDictionaryRef InterfaceRankAssertionsCopy(void) { CFMutableDictionaryRef assertions = NULL; ControlSessionRef session; LIST_FOREACH(session, &S_ControlSessions, link) { if (session->advisories != NULL) { CFDictionaryApplyFunction(session->advisories, InterfaceAdvisoryAdd, &assertions); } if (session->assertions != NULL) { CFDictionaryApplyFunction(session->assertions, InterfaceRankAssertionAdd, &assertions); } } return (assertions); } STATIC Boolean InterfaceHasAdvisories(CFStringRef ifname) { ControlSessionRef session; LIST_FOREACH(session, &S_ControlSessions, link) { if (session->advisories != NULL && CFDictionaryContainsKey(session->advisories, ifname)) { return (TRUE); } } return (FALSE); } STATIC AWDIPMonitorInterfaceAdvisoryReport_Flags advisory_to_flags(SCNetworkInterfaceAdvisory advisory) { AWDIPMonitorInterfaceAdvisoryReport_Flags flags; switch (advisory) { case kSCNetworkInterfaceAdvisoryNone: default: flags = 0; break; case kSCNetworkInterfaceAdvisoryLinkLayerIssue: flags = AWDIPMonitorInterfaceAdvisoryReport_Flags_LINK_LAYER_ISSUE; break; case kSCNetworkInterfaceAdvisoryUplinkIssue: flags = AWDIPMonitorInterfaceAdvisoryReport_Flags_UPLINK_ISSUE; break; } return (flags); } STATIC AWDIPMonitorInterfaceAdvisoryReport_Flags InterfaceGetAdvisoryFlags(CFStringRef ifname, ControlSessionRef exclude_session, uint32_t * ret_count) { uint32_t count; AWDIPMonitorInterfaceAdvisoryReport_Flags flags = 0; ControlSessionRef session; count = 0; LIST_FOREACH(session, &S_ControlSessions, link) { SCNetworkInterfaceAdvisory advisory = 0; CFNumberRef advisory_cf; if (session->advisories == NULL) { continue; } if (exclude_session != NULL && exclude_session == session) { continue; } advisory_cf = CFDictionaryGetValue(session->advisories, ifname); if (advisory_cf == NULL) { /* session has no advisories for this interface */ continue; } (void)CFNumberGetValue(advisory_cf, kCFNumberSInt32Type, &advisory); flags |= advisory_to_flags(advisory); count++; } *ret_count = count; return (flags); } STATIC Boolean AnyInterfaceHasAdvisories(void) { ControlSessionRef session; LIST_FOREACH(session, &S_ControlSessions, link) { if (session->advisories != NULL) { return (TRUE); } } return (FALSE); } STATIC CFRunLoopRef S_runloop; STATIC CFRunLoopSourceRef S_signal_source; STATIC void SetNotificationInfo(CFRunLoopRef runloop, CFRunLoopSourceRef rls) { S_runloop = runloop; S_signal_source = rls; return; } STATIC void NotifyIPMonitor(void) { if (S_signal_source != NULL) { CFRunLoopSourceSignal(S_signal_source); if (S_runloop != NULL) { CFRunLoopWakeUp(S_runloop); } } return; } STATIC void NotifyInterfaceAdvisory(CFStringRef ifname) { CFStringRef key; key = _IPMonitorControlCopyInterfaceAdvisoryNotificationKey(ifname); SCDynamicStoreNotifyValue(NULL, key); CFRelease(key); return; } STATIC void SubmitInterfaceAdvisoryMetric(CFStringRef ifname, AWDIPMonitorInterfaceAdvisoryReport_Flags flags, uint32_t count) { InterfaceAdvisoryReportRef report; AWDIPMonitorInterfaceType type; /* XXX need to actually figure out what the interface type is */ if (CFStringHasPrefix(ifname, CFSTR("pdp"))) { type = AWDIPMonitorInterfaceType_IPMONITOR_INTERFACE_TYPE_CELLULAR; } else { type = AWDIPMonitorInterfaceType_IPMONITOR_INTERFACE_TYPE_WIFI; } report = InterfaceAdvisoryReportCreate(type); if (report == NULL) { return; } InterfaceAdvisoryReportSetFlags(report, flags); InterfaceAdvisoryReportSetAdvisoryCount(report, count); InterfaceAdvisoryReportSubmit(report); my_log(LOG_NOTICE, "%@: submitted AWD report %@", ifname, report); CFRelease(report); } /** ** ControlSession **/ STATIC void AddChangedInterface(const void * key, const void * value, void * context) { #pragma unused(value) #pragma unused(context) InterfaceChangedListAddInterface((CFStringRef)key); return; } STATIC void AddChangedInterfaceNotify(const void * key, const void * value, void * context) { #pragma unused(value) #pragma unused(context) InterfaceChangedListAddInterface((CFStringRef)key); NotifyInterfaceAdvisory((CFStringRef)key); return; } STATIC void GenerateMetricForInterfaceAtSessionClose(const void * key, const void * value, void * context) { uint32_t count_after; uint32_t count_before; AWDIPMonitorInterfaceAdvisoryReport_Flags flags_after; AWDIPMonitorInterfaceAdvisoryReport_Flags flags_before; CFStringRef ifname = (CFStringRef)key; ControlSessionRef session = (ControlSessionRef)context; #pragma unused(value) /* * Get the flags and count including this session, then again * excluding this session. If either flags or count are different, * generate the metric. */ flags_before = InterfaceGetAdvisoryFlags(ifname, NULL, &count_before); flags_after = InterfaceGetAdvisoryFlags(ifname, session, &count_after); if (flags_before != flags_after || count_before != count_after) { SubmitInterfaceAdvisoryMetric(ifname, flags_after, count_after); } return; } STATIC void ControlSessionGenerateMetricsAtClose(ControlSessionRef session) { if (session->advisories == NULL) { return; } CFDictionaryApplyFunction(session->advisories, GenerateMetricForInterfaceAtSessionClose, session); } STATIC void ControlSessionInvalidate(ControlSessionRef session) { my_log(LOG_DEBUG, "Invalidating %p", session); ControlSessionGenerateMetricsAtClose(session); LIST_REMOVE(session, link); if (session->assertions != NULL || session->advisories != NULL) { if (session->advisories != NULL) { my_log(LOG_NOTICE, "pid %d removing advisories %@", xpc_connection_get_pid(session->connection), session->advisories); CFDictionaryApplyFunction(session->advisories, AddChangedInterfaceNotify, NULL); my_CFRelease(&session->advisories); } if (session->assertions != NULL) { my_log(LOG_NOTICE, "pid %d removing assertions %@", xpc_connection_get_pid(session->connection), session->assertions); CFDictionaryApplyFunction(session->assertions, AddChangedInterface, NULL); my_CFRelease(&session->assertions); } NotifyIPMonitor(); } return; } STATIC void ControlSessionRelease(void * p) { my_log(LOG_DEBUG, "Releasing %p", p); free(p); return; } STATIC ControlSessionRef ControlSessionLookup(xpc_connection_t connection) { return ((ControlSessionRef)xpc_connection_get_context(connection)); } STATIC ControlSessionRef ControlSessionCreate(xpc_connection_t connection) { ControlSessionRef session; session = (ControlSessionRef)malloc(sizeof(*session)); memset(session, 0, sizeof(*session)); session->connection = connection; xpc_connection_set_finalizer_f(connection, ControlSessionRelease); xpc_connection_set_context(connection, session); LIST_INSERT_HEAD(&S_ControlSessions, session, link); my_log(LOG_DEBUG, "Created %p (connection %p)", session, connection); return (session); } STATIC ControlSessionRef ControlSessionForConnection(xpc_connection_t connection) { ControlSessionRef session; session = ControlSessionLookup(connection); if (session != NULL) { return (session); } return (ControlSessionCreate(connection)); } STATIC void ControlSessionSetInterfaceRank(ControlSessionRef session, const char * ifname, SCNetworkServicePrimaryRank rank) { CFStringRef ifname_cf; if (session->assertions == NULL) { if (rank == kSCNetworkServicePrimaryRankDefault) { /* no assertions, no need to store rank */ return; } session->assertions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } ifname_cf = CFStringCreateWithCString(NULL, ifname, kCFStringEncodingUTF8); if (rank == kSCNetworkServicePrimaryRankDefault) { CFDictionaryRemoveValue(session->assertions, ifname_cf); if (CFDictionaryGetCount(session->assertions) == 0) { CFRelease(session->assertions); session->assertions = NULL; } } else { CFNumberRef rank_cf; rank_cf = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank); CFDictionarySetValue(session->assertions, ifname_cf, rank_cf); CFRelease(rank_cf); } InterfaceChangedListAddInterface(ifname_cf); NotifyIPMonitor(); CFRelease(ifname_cf); return; } STATIC SCNetworkServicePrimaryRank ControlSessionGetInterfaceRank(ControlSessionRef session, const char * ifname) { SCNetworkServicePrimaryRank rank = kSCNetworkServicePrimaryRankDefault; if (session->assertions != NULL) { CFStringRef ifname_cf; CFNumberRef rank_cf; ifname_cf = CFStringCreateWithCString(NULL, ifname, kCFStringEncodingUTF8); rank_cf = CFDictionaryGetValue(session->assertions, ifname_cf); CFRelease(ifname_cf); if (rank_cf != NULL) { (void)CFNumberGetValue(rank_cf, kCFNumberSInt32Type, &rank); } } return (rank); } STATIC void ControlSessionSetInterfaceAdvisory(ControlSessionRef session, const char * ifname, SCNetworkInterfaceAdvisory advisory) { uint32_t count_after; uint32_t count_before; AWDIPMonitorInterfaceAdvisoryReport_Flags flags_after; AWDIPMonitorInterfaceAdvisoryReport_Flags flags_before; CFStringRef ifname_cf; if (session->advisories == NULL) { if (advisory == kSCNetworkInterfaceAdvisoryNone) { /* no advisories, no need to store advisory */ return; } session->advisories = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } ifname_cf = CFStringCreateWithCString(NULL, ifname, kCFStringEncodingUTF8); flags_before = InterfaceGetAdvisoryFlags(ifname_cf, NULL, &count_before); if (advisory == kSCNetworkInterfaceAdvisoryNone) { CFDictionaryRemoveValue(session->advisories, ifname_cf); if (CFDictionaryGetCount(session->advisories) == 0) { CFRelease(session->advisories); session->advisories = NULL; } } else { CFNumberRef advisory_cf; advisory_cf = CFNumberCreate(NULL, kCFNumberSInt32Type, &advisory); CFDictionarySetValue(session->advisories, ifname_cf, advisory_cf); CFRelease(advisory_cf); } flags_after = InterfaceGetAdvisoryFlags(ifname_cf, NULL, &count_after); if (flags_before != flags_after || count_before != count_after) { SubmitInterfaceAdvisoryMetric(ifname_cf, flags_after, count_after); } InterfaceChangedListAddInterface(ifname_cf); NotifyInterfaceAdvisory(ifname_cf); NotifyIPMonitor(); CFRelease(ifname_cf); return; } /** ** IPMonitorControlServer **/ STATIC const char * get_process_name(xpc_object_t request) { const char * process_name; process_name = xpc_dictionary_get_string(request, kIPMonitorControlRequestKeyProcessName); if (process_name == NULL) { process_name = ""; } return (process_name); } STATIC Boolean IPMonitorControlServerConnectionIsRoot(xpc_connection_t connection) { uid_t uid; uid = xpc_connection_get_euid(connection); return (uid == 0); } STATIC Boolean IPMonitorControlServerConnectionHasEntitlement(xpc_connection_t connection, const char * entitlement) { Boolean entitled = FALSE; xpc_object_t val; val = xpc_connection_copy_entitlement_value(connection, entitlement); if (val != NULL) { if (xpc_get_type(val) == XPC_TYPE_BOOL) { entitled = xpc_bool_get_value(val); } xpc_release(val); } return (entitled); } STATIC const char * get_rank_str(SCNetworkServicePrimaryRank rank) { const char * str = NULL; switch (rank) { case kSCNetworkServicePrimaryRankDefault: str = "Default"; break; case kSCNetworkServicePrimaryRankFirst: str = "First"; break; case kSCNetworkServicePrimaryRankLast: str = "Last"; break; case kSCNetworkServicePrimaryRankNever: str = "Never"; break; case kSCNetworkServicePrimaryRankScoped: str = "Scoped"; break; default: break; } return (str); } STATIC int HandleSetInterfaceRank(xpc_connection_t connection, xpc_object_t request, xpc_object_t reply) { #pragma unused(reply) const char * ifname; SCNetworkServicePrimaryRank rank; const char * rank_str; ControlSessionRef session; if (!IPMonitorControlServerConnectionIsRoot(connection)) { my_log(LOG_INFO, "connection %p pid %d permission denied", connection, xpc_connection_get_pid(connection)); return (EPERM); } ifname = xpc_dictionary_get_string(request, kIPMonitorControlRequestKeyInterfaceName); if (ifname == NULL) { return (EINVAL); } rank = (SCNetworkServicePrimaryRank) xpc_dictionary_get_uint64(request, kIPMonitorControlRequestKeyPrimaryRank); rank_str = get_rank_str(rank); if (rank_str == NULL) { return (EINVAL); } session = ControlSessionForConnection(connection); ControlSessionSetInterfaceRank(session, ifname, rank); my_log(LOG_NOTICE, "%s[%d] SetInterfaceRank(%s) = %s (%u)", get_process_name(request), xpc_connection_get_pid(connection), ifname, rank_str, rank); return (0); } STATIC int HandleGetInterfaceRank(xpc_connection_t connection, xpc_object_t request, xpc_object_t reply) { const char * ifname; SCNetworkServicePrimaryRank rank; ControlSessionRef session; if (reply == NULL) { /* no point in processing the request if we can't provide an answer */ return (EINVAL); } session = ControlSessionLookup(connection); if (session == NULL) { /* no session, no rank assertion */ return (ENOENT); } ifname = xpc_dictionary_get_string(request, kIPMonitorControlRequestKeyInterfaceName); if (ifname == NULL) { return (EINVAL); } rank = ControlSessionGetInterfaceRank(session, ifname); xpc_dictionary_set_uint64(reply, kIPMonitorControlResponseKeyPrimaryRank, rank); return (0); } STATIC const char * get_advisory_str(SCNetworkInterfaceAdvisory advisory) { const char * str = NULL; switch (advisory) { case kSCNetworkInterfaceAdvisoryNone: str = "None"; break; case kSCNetworkInterfaceAdvisoryLinkLayerIssue: str = "LinkLayerIssue"; break; case kSCNetworkInterfaceAdvisoryUplinkIssue: str = "UplinkIssue"; break; default: break; } return (str); } STATIC int HandleSetInterfaceAdvisory(xpc_connection_t connection, xpc_object_t request, xpc_object_t reply) { #pragma unused(reply) SCNetworkInterfaceAdvisory advisory; const char * advisory_str; const char * ifname; const char * reason; ControlSessionRef session; #define ENTITLEMENT "com.apple.SystemConfiguration.SCNetworkInterfaceSetAdvisory" if (!IPMonitorControlServerConnectionIsRoot(connection) && !IPMonitorControlServerConnectionHasEntitlement(connection, ENTITLEMENT)) { my_log(LOG_INFO, "connection %p pid %d permission denied", connection, xpc_connection_get_pid(connection)); return (EPERM); } ifname = xpc_dictionary_get_string(request, kIPMonitorControlRequestKeyInterfaceName); if (ifname == NULL) { return (EINVAL); } reason = xpc_dictionary_get_string(request, kIPMonitorControlRequestKeyReason); advisory = (SCNetworkInterfaceAdvisory) xpc_dictionary_get_uint64(request, kIPMonitorControlRequestKeyAdvisory); /* validate the advisory code */ advisory_str = get_advisory_str(advisory); if (advisory_str == NULL) { return (EINVAL); } session = ControlSessionForConnection(connection); ControlSessionSetInterfaceAdvisory(session, ifname, advisory); my_log(LOG_NOTICE, "%s[%d] SetInterfaceAdvisory(%s) = %s (%u) reason='%s'", get_process_name(request), xpc_connection_get_pid(connection), ifname, advisory_str, advisory, reason != NULL ? reason : "" ); return (0); } STATIC int HandleInterfaceAdvisoryIsSet(xpc_connection_t connection, xpc_object_t request, xpc_object_t reply) { #pragma unused(connection) const char * ifname; CFStringRef ifname_cf; if (reply == NULL) { /* no point in processing the request if we can't provide an answer */ return (EINVAL); } ifname = xpc_dictionary_get_string(request, kIPMonitorControlRequestKeyInterfaceName); if (ifname == NULL) { return (EINVAL); } ifname_cf = CFStringCreateWithCString(NULL, ifname, kCFStringEncodingUTF8); xpc_dictionary_set_bool(reply, kIPMonitorControlResponseKeyAdvisoryIsSet, InterfaceHasAdvisories(ifname_cf)); CFRelease(ifname_cf); return (0); } STATIC int HandleAnyInterfaceAdvisoryIsSet(xpc_connection_t connection, xpc_object_t request, xpc_object_t reply) { #pragma unused(connection) #pragma unused(request) if (reply == NULL) { /* no point in processing the request if we can't provide an answer */ return (EINVAL); } xpc_dictionary_set_bool(reply, kIPMonitorControlResponseKeyAdvisoryIsSet, AnyInterfaceHasAdvisories()); return (0); } STATIC void IPMonitorControlServerHandleDisconnect(xpc_connection_t connection) { ControlSessionRef session; my_log(LOG_DEBUG, "IPMonitorControlServer: client %p went away", connection); session = ControlSessionLookup(connection); if (session == NULL) { /* never asserted anything */ return; } ControlSessionInvalidate(session); return; } STATIC void IPMonitorControlServerHandleRequest(xpc_connection_t connection, xpc_object_t request) { xpc_type_t type; type = xpc_get_type(request); if (type == XPC_TYPE_DICTIONARY) { int error = 0; uint64_t request_type; xpc_connection_t remote; xpc_object_t reply; request_type = xpc_dictionary_get_uint64(request, kIPMonitorControlRequestKeyType); reply = xpc_dictionary_create_reply(request); switch (request_type) { case kIPMonitorControlRequestTypeSetInterfaceRank: error = HandleSetInterfaceRank(connection, request, reply); break; case kIPMonitorControlRequestTypeGetInterfaceRank: error = HandleGetInterfaceRank(connection, request, reply); break; case kIPMonitorControlRequestTypeSetInterfaceAdvisory: error = HandleSetInterfaceAdvisory(connection, request, reply); break; case kIPMonitorControlRequestTypeInterfaceAdvisoryIsSet: error = HandleInterfaceAdvisoryIsSet(connection, request, reply); break; case kIPMonitorControlRequestTypeAnyInterfaceAdvisoryIsSet: error = HandleAnyInterfaceAdvisoryIsSet(connection, request, reply); break; default: error = EINVAL; break; } if (reply == NULL) { /* client didn't want a reply */ return; } xpc_dictionary_set_int64(reply, kIPMonitorControlResponseKeyError, error); remote = xpc_dictionary_get_remote_connection(request); xpc_connection_send_message(remote, reply); xpc_release(reply); } else if (type == XPC_TYPE_ERROR) { if (request == XPC_ERROR_CONNECTION_INVALID) { IPMonitorControlServerHandleDisconnect(connection); } else if (request == XPC_ERROR_CONNECTION_INTERRUPTED) { my_log(LOG_INFO, "connection interrupted"); } } else { my_log(LOG_NOTICE, "unexpected event"); } return; } STATIC void IPMonitorControlServerHandleNewConnection(xpc_connection_t connection) { xpc_handler_t handler; handler = ^(xpc_object_t event) { IPMonitorControlServerHandleRequest(connection, event); }; xpc_connection_set_event_handler(connection, handler); xpc_connection_set_target_queue(connection, S_IPMonitorControlServerQueue); xpc_connection_resume(connection); return; } STATIC xpc_connection_t IPMonitorControlServerCreate(dispatch_queue_t queue, const char * name) { uint64_t flags = XPC_CONNECTION_MACH_SERVICE_LISTENER; xpc_connection_t connection; xpc_handler_t handler; connection = xpc_connection_create_mach_service(name, queue, flags); if (connection == NULL) { return (NULL); } handler = ^(xpc_object_t event) { xpc_type_t type; type = xpc_get_type(event); if (type == XPC_TYPE_CONNECTION) { IPMonitorControlServerHandleNewConnection(event); } else if (type == XPC_TYPE_ERROR) { const char * desc; desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION); if (event == XPC_ERROR_CONNECTION_INVALID) { my_log(LOG_NOTICE, "%s", desc); xpc_release(connection); } else { my_log(LOG_NOTICE, "%s", desc); } } else { my_log(LOG_NOTICE, "unknown event %p", type); } }; S_IPMonitorControlServerQueue = queue; xpc_connection_set_event_handler(connection, handler); xpc_connection_resume(connection); return (connection); } PRIVATE_EXTERN Boolean IPMonitorControlServerStart(CFRunLoopRef runloop, CFRunLoopSourceRef rls, Boolean * verbose) { #pragma unused(verbose) dispatch_queue_t q; xpc_connection_t connection; SetNotificationInfo(runloop, rls); q = dispatch_queue_create("IPMonitorControlServer", NULL); connection = IPMonitorControlServerCreate(q, kIPMonitorControlServerName); if (connection == NULL) { my_log(LOG_ERR, "IPMonitorControlServer: failed to create server"); dispatch_release(q); return (FALSE); } return (TRUE); } PRIVATE_EXTERN CFArrayRef IPMonitorControlServerCopyInterfaceRankInformation(CFDictionaryRef * info) { __block CFArrayRef changed; __block CFDictionaryRef dict; dispatch_sync(S_IPMonitorControlServerQueue, ^{ dict = InterfaceRankAssertionsCopy(); changed = InterfaceChangedListCopy(); }); *info = dict; return (changed); }