Hi, have a similar scenario, and have been trying to do this for quite some time, to no avail (my "Agent" isn't a UI-session, but still runs as a daemon on the user's session).
My global-daemon (runs in the system domain as root) receives connection from my "Agent",
"Agent" can send messages to daemon successfully, and receive results.
Now if you could be so kind as to space a few lines of code explaining what it means to "send the anonymous XPC listener endpoint to the daemon" and how can Daemon use this endpoint. I tried several things and they didn't work for me so far.
In my case, it's not a "one time" job. Agent connects to Daemon and Daemon needs to regularly report its actions related to the Agent's
user (I can have an agent for each user logged in).
My current state of code (not working) is:
Agent side creating connection:
Code Block ObjectiveC// lazy property getter |
- (NSXPCConnection *) myXPCConnection { |
// Create the XPC Connection on demand |
if (_myXPCConnection == nil) { |
_myXPCConnection = [[NSXPCConnection alloc] initWithMachServiceName:preventionServiceLabel options:NSXPCConnectionPrivileged]; |
_myXPCConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(myXPCProtocol)]; |
|
// Attempt to create a bi-directional connection, so to receive reports from the XPC service. |
_myXPCConnection.exportedInterface = NSXPCInterface interfaceWithProtocol:@protocol(OITPreventionXPCProtocol)]; |
_myXPCConnection.exportedObject = self; |
|
_myXPCConnection.invalidationHandler = ^{ // on invalidation we simply nullify connection reference, so it will be re-created once app tries to use it again. |
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ |
#pragma clang diagnostic push |
#pragma clang diagnostic ignored "-Warc-retain-cycles" |
self.myXPCConnection.invalidationHandler = nil; |
self.myXPCConnection = nil; |
os_log(preventionUILog, "connection has been invalidated"); |
#pragma clang diagnostic pop |
}]; |
}; |
_myXPCConnection.interruptionHandler = ^{ // on interruption we simply nullify connection reference, so it will be re-created once app tries to use it again. |
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ |
#pragma clang diagnostic push |
#pragma clang diagnostic ignored "-Warc-retain-cycles" |
self.myXPCConnection.interruptionHandler = nil; |
self.myXPCConnection = nil; |
os_log(preventionUILog, "connection interrupted"); |
#pragma clang diagnostic pop |
}]; |
}; |
[_myXPCConnection resume]; // New connections always crated and starts in a suspended state |
} |
return _myXPCConnection; |
} |
Daemon side:
Code Block ObjectiveC- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { |
assert(listener == _listener); |
assert(newConnection != nil); |
|
BOOL accept = NO; |
do { |
// Verify client identity, only accept if client is code-signed by our teamID and if its signingID is of our Agent. |
|
if (/* not allowed connection.... then */) |
break; |
accept = YES; // we accept connections from processes signed by our development team (proofpoint) |
|
// now see who's calling. |
if (/* it is Agent */) |
[self.monitor agentConnected:newConnection]; |
|
// Configure incoming connection. First, set the interface that the exported object implements. |
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(OITPreventionXPCProtocol)]; |
|
// Next set self to be the object the connection exports. All messages sent on the connection to this service will be sent to self to handle. The connection retains the exported object. |
newConnection.exportedObject = self; |
|
newConnection.invalidationHandler = ^{ |
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ |
#pragma clang diagnostic push |
#pragma clang diagnostic ignored "-Warc-retain-cycles" |
[self.monitor agentDisconnectedForUser:newConnection.effectiveUserIdentifier]; |
#pragma clang diagnostic pop |
}]; |
}; |
|
newConnection.interruptionHandler = ^{ |
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ |
#pragma clang diagnostic push |
#pragma clang diagnostic ignored "-Warc-retain-cycles" |
os_log_info(myLog, "Agent Connection interrupted."); |
#pragma clang diagnostic pop |
}]; |
}; |
os_log_info(myLog, "XPCListener accepted XPC connection from pid: %d", newConnection.processIdentifier); |
[newConnection resume]; // New connections always start in a suspended state, start it |
} while(false); |
return accept; // Returning YES from this method tells the system that you have accepted this connection. |
} |
and last - when Connection is established, and after Daemon receives a message call and replies to Daemon - I try to use the connection from the Daemon side like this:
Code Block ObjectiveC-(void)reportPrevention:(MYEventESInfo * _Nonnull)eventInfo { |
uid_t user = eventInfo.processInfo.auid; |
NSXPCConnection *agentConnection = [self.connectedAgents objectForKey:@(user)]; |
if (agentConnection != nil) { |
|
[agentConnection.remoteObjectProxy eventHandled:[eventInfo dictionary]]; // <<<<< HERE I CRASH WITH EXCEPTION |
} |
else { |
os_log(monitoringLog, "No Agent for Event: %{public}@", eventInfo ); |
} |
} |
Objective-C exception : method 'eventHandled:' unknown to remoteObjectProxy.
Any idea what's wrong?