IPMonitorControlServer.c [plain text]
#include <CoreFoundation/CoreFoundation.h>
#include <xpc/xpc.h>
#include <xpc/private.h>
#include <sys/queue.h>
#include <CoreFoundation/CFRunLoop.h>
#include <SystemConfiguration/SCNetworkConfigurationPrivate.h>
#include <SystemConfiguration/SCPrivate.h>
#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
#include "ip_plugin.h"
#endif
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;
CFMutableDictionaryRef advisories;
};
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;
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) {
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;
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);
}
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)
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) {
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) {
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;
}
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 = "<unknown>";
}
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) {
return (EINVAL);
}
session = ControlSessionLookup(connection);
if (session == NULL) {
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);
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) {
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) {
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) {
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) {
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);
}