XPC is a a low-level (libSystem) interprocess communication mechanism that is based on serialized property lists.

Posts under XPC tag

200 Posts

Post

Replies

Boosts

Views

Activity

Notification on NSUserDefaults Change When App Is In Background
I have two applications that share an app group @"group.edu.tds.poc.shared"Using NSUserDefaults of this app group, I am able to exchange data between the reader app and the writer app.The Writer App writes data to the NSUserDefaultNSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.edu.tds.poc.shared"]; [share dDefaults setObject:stringToStore forKey:key]; [sharedDefaults synchronize];The Reader App reads the data from the NSUserDefaultNSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.edu.tds.poc.shared"]; return [sharedDefaults stringForKey:@"key"];I now want the reader app to be notified as soon as the writer app modifies the value for the key. I.e. Can I notify the reader app when the writer app is running in the foreground (Reader app is either running in the background or is not running).I have implemented the marked solution but with no luck:Cocoa - Notification on NSUserDefaults value change?NSUserDefaults and KVO issuesCall back only when the Reader App is in foreground state. (Wrote to the Shared Defaults using Reader app)Any idea if this can be achieved?
6
1
3.1k
Nov ’22
Communicating between System Extension (specifically, camera extension) and Container App
i am trying to send data from the container app to its system extension (specifically, a camera extension. i am not entirely sure how to go about this. i read that i could utilize XPC, and i have tried this but for some reason, the system extension returns nothing when trying to connect to the XPC Service. below is an example code snipper let connectionToService = NSXPCConnection(serviceName: "example.VirtualCameraXPC")     connectionToService.remoteObjectInterface = NSXPCInterface(with:VirtualCameraXPCProtocol.self)     connectionToService.resume() //checking processes available shows that the XPC Server process is never activated also. if let proxy = connectionToService.remoteObjectProxy as? VirtualCameraXPCProtocol {       proxy.getNewString() { aString in //this is never logged.         NSLog("Second result string was: \(aString)")       }     } when i run the above code from the container app, it works properly but from the system extension, i run into the problems commented above in the code block. i would appreciate any help that will help me successfully send data from my container app to my camera extension.
1
0
1.2k
Nov ’22
Communication between system extension and the app
I have a working NEPacketTunnelProvider app extension macOS app on the App Store. The company wants to explore the possibility of switching to system extension, so that we can distribute the app outside of the appstore too. I managed to do the switch and the extension works. But the communication is broken. DistributedNotificationCenter stopped working for me after switching to system extension, events are not received and I don't see any errors so I cannot say what's wrong. I tried to adopt XPC from this Filtering Network Traffic Apple's sample, but I get sandbox error - domain code 4099, failed at lookup with error 159 - Sandbox restriction. I get the same error if I try to run the sample with my company team id. I do these changes: NEMachServiceName to $(TeamIdentifierPrefix)com.mycompanyname.macos.dev App Groups to $(TeamIdentifierPrefix)com.mycompanyname.macos.dev Bundle ids to com.mycompanyname.macos.dev and com.mycompanyname.macos.dev.tunnelprovider com.mycompanyname.macos.dev has capabilities - App Groups, Network Extensions, System Extensions com.mycompanyname.macos.dev.tunnelprovider - Network Extensions, System Extensions Could you help me find the reason why DistributedNotificationCenter could stop receiving notifications? Or are you able to run Apple's sample? What changes do you make to run it under your team? Because it looks like my changes are wrong Either DistributedNotificationCenter or XPC would solve my problem
3
1
1.4k
Nov ’22
XPC Connection Not Invalidated on NSXPCListenerDelegate Denial
I am attempting to understand the expected behavior of several points related to XPC. Mainly, I have the following questions: Should I expect that NSXPCConnection.remoteObjectProxyWithErrorHandler to call the error handler if the associated NSXPCListenerDelegate returns false in its listener function? Does the error handler get called immediately if the remoteObjectProxyWithErrorHandler function fails? What does remoteObjectProxy return if an actual proxy object is never exported on the service-side (in the listener function)? Should I expect that NSXPCConnection.invalidate() and/or the connection's invalidationHandler to be called when the associated NSXPCListenerDelegate returns false in its listener function? According to the listener documentation below, it appears the listener function is supposed to invalidate the connection; however, I am not seeing this be the case. https://developer.apple.com/documentation/foundation/nsxpclistenerdelegate/1410381-listener To reject the connect, return a value of false. This causes the connection object to be invalidated. I have written a test that exhibits the fact when the listener function returns false, rather than the invalidationHandler being called, the interruptionHandler is called. import XCTest final class InvalidateTest: XCTestCase {   func testConnectionIsInvalidatedOnListenerRejection() {     //set up anonymous XPC Listener     let listener = NSXPCListener.anonymous()     let listenerDelegate = MockListenerDelegate()     listener.delegate = listenerDelegate     listener.resume()           //establish connection to service     let clientConnection = MockConnection(listenerEndpoint: listener.endpoint)     var interruptHandlerCalled = false     var invalidateHandlerCalled = false     let interruptionHandler = { interruptHandlerCalled = true }     let invalidationHandler = { invalidateHandlerCalled = true }     clientConnection.interruptionHandler = interruptionHandler     clientConnection.invalidationHandler = invalidationHandler     clientConnection.remoteObjectInterface = NSXPCInterface(with: XPCServDelegate.self)     clientConnection.exportedInterface = NSXPCInterface(with: XPCCliDelegate.self)     clientConnection.exportedObject = XPCCDelegate()     clientConnection.resume()           // get the proxy delegate to make the XPC pulse call     guard let proxy = clientConnection.remoteObjectProxy() as? XPCServDelegate else {       XCTAssert(false, "Unable to get proxy object")       return     }     // why is it not failing to get a proxy object in this case?           // make the pulse call     var replyCalled = false     let semaphore = DispatchSemaphore(value: 0)     proxy.pulse(reply: {       replyCalled = true       semaphore.signal()     })           let waitResult = semaphore.wait(timeout: .now() + 1)     XCTAssertEqual(waitResult, .timedOut)     XCTAssertFalse(replyCalled)           // why do these assertions fail?     XCTAssertTrue(clientConnection.invalidateCalled)     XCTAssertTrue(invalidateHandlerCalled)     XCTAssertFalse(interruptHandlerCalled)   } } @objc public protocol XPCServDelegate {   func pulse(reply: @escaping () -> Void) } class XPCSDelegate : XPCServDelegate {   func pulse(reply: @escaping () -> Void) {     reply()   } } @objc public protocol XPCCliDelegate {} class XPCCDelegate : XPCCliDelegate {} fileprivate class MockConnection : NSXPCConnection {   var invalidateCalled = false       override func invalidate() {     invalidateCalled = true     super.invalidate()   } } fileprivate class MockListenerDelegate : NSObject, NSXPCListenerDelegate {   func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {     return false   } }
0
0
1.8k
Nov ’22
XPC between Container App and System Extension
I'm trying to create an XPC service to communicate between my Endpoint Security Extension and its Container App. I've taken the Sample Endpoint App from here. I've then followed the steps under Creating the Service here In fact, when I added the XPC service via the template, Xcode automatically added an Embed XPC Services phase to the container app. I can confirm that in the built container app I see the xpc service: SampleEndpointApp.app/Contents/XPCServices/Service.xpc If I initiate an NSXPCConnection from the container app then I can both connect and make RPCs. Furthermore I see the service process running via ps and also launchtl. If however I try to initiate an NSXPCConnection from the extension then I see nothing. RPC doesn't work and I don't see the service being launched. I've tried this with and without the connection in the main app. What am I missing here? What needs to be done to allow both processes to talk to each other? Is there some permissions issue here? Note that my plist for the service is as follows:
13
0
3.7k
Oct ’22
How to publish an XPC Service in a global daemon that employs EndpointSecurity framework?
I have a global daemon managed by launchd, whose .plist is installed in /Library/LaunchDaemons). To be correctly entitled and code-signed so it can communicate with EndpointSecurity framework, its executable resides in a normal Mac App bundle (main() will run as minimal UI when launched from UI, and as a daemon when launched by launchd). This means that the ProgramArguments.0 in its .plist looks something like /Library/PrivilegedHelperTools/MyDaemonApp.app/Contents/MacOS/MyDaemonApp Now I need this daemon to publish an XPC Service (with few control commands) so that other components of our system (UI app, a user-context launchd-daemon and another launchd global-daemon) will be able to connect to The XPC Service and control it via the published protocol. I read some answers here, and also found a working order sample code that does just this here - https://github.com/jdspoone/SampleOSXLaunchDaemon But when I apply its content to my global daemon, word for word - it doesn't work - meaning, clients cannot create a connection to The XPC Service. The daemon is up and running, and functional. its .plist is quite simple and looks like this: <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.mycompany.itm.service</string> <key>KeepAlive</key> <true/> <key>RunAtLoad</key> <true/> <key>MachServices</key> <dict> <key>com.mycompany.itm.service</key> <true/> </dict> <key>ProgramArguments</key> <array> <string>/Library/PrivilegedHelperTools/IMyDaemonApp.app/Contents/MacOS/MyDaemonApp</string> <string>-monitor</string> <string>-protectDeviceProtocol</string> <string>USB</string> </array> </dict> </plist> It creates and starts an XPC listener in MYXPCListener.h like thus: #import <Foundation/Foundation.h> #import "MYXPCProtocol.h" NS_ASSUME_NONNULL_BEGIN @interface OITPreventionXPCService : NSObject (instancetype) init; (void) start; /* Begin listening for incoming XPC connections */ (void) stop; /* Stop listening for incoming XPC connections */ @end NS_ASSUME_NONNULL_END and the implementation is: /* AppDelegate.m */ @interface MYXPCService () <NSXPCListenerDelegate, OITPreventionXPCProtocol> @property (nonatomic, strong, readwrite) NSXPCListener *listener; @property (nonatomic, readwrite) BOOL started; @end @implementation OITPreventionXPCService (instancetype) init {     if ((self = [super init]) != nil) {         _listener = [[NSXPCListener alloc] initWithMachServiceName:@"com.mycompany.itm.service"];         _listener.delegate = self;         if (_listener == nil) {             os_log_error(myLog, "XPCListener failed to initialize");         }         _started = NO;     }     return self; } (void) start {     assert(_started == NO);     [_listener resume];     os_log_info(myLog, "XPCListener resumed");     _started = YES; } (void) stop {     assert(_started == YES);     [_listener suspend];     os_log_info(myLog, "XPCListener suspended");     _started = NO; } /* NSXPCListenerDelegate implementation */ (BOOL) listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {     os_log_info(myLog, "Prevention XPCListener is bequsted a new connection");     assert(listener == _listener);     assert(newConnection != nil);     newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MYXPCProtocol)];     newConnection.exportedObject = self; &#9;&#9;[newConnection resume];     return YES; } /* Further down this implementation, I have implementations to all the methods in MYXPCProtocol. */ @end Now the client code (and I tried EVERY kind of client, signed unsigned, daemon, UI, root privileged, or user-scoped - whatever). For example, in the AppDelegate of a UI app: #import "AppDelegate.h" #import "MYXPCProtocol.h" @interface AppDelegate () @property (strong) IBOutlet NSWindow *window; @property (nonatomic, strong, readwrite) NSXPCConnection *connection; /* lazy initialized */ @end @implementation AppDelegate (NSXPCConnection *) connection {     if (_connection == nil) {         _connection = [[NSXPCConnection alloc] initWithMachServiceName:daemonLabel options:NSXPCConnectionPrivileged];         _connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MYXPCProtocol)];         _connection.invalidationHandler =  ^{             self->_connection = nil;             NSLog(@"connection has been invalidated");         };         [_connection resume];         /* New connections always start suspended */     }     return _connection; } (IBAction) getServiceStatus:(id)sender {     [self.connection.remoteObjectProxy getStatus:^(NSString * _Nonnull status) {         NSLog(@"MY XPC Service status is: %@", status);     }]; } @end but no matter what I do - I always get the "connection invalidated". The sample launchDaemon that works - is not code-signed at all!!! but mine, which is both signed and checking of which yields $ spctl --assess --verbose IMyDaemonApp.app IMyDaemonApp.app: accepted source=Notarized Developer ID   I'm at a loss - and would like to get any advice, or any helpful documentation (I've been reading TN2083, and man launchctl and man launchd.plist and many other pages - to no avail. There seems to be no real "programming guide" for XPC and no reasonable sample code on Apple developer site to fit my needs. Last - this is MacOS 10.15.7, and latest Xcode 12.3
7
0
3.0k
Oct ’22
Allowing dynamic name registration in xpc_connection_create_mach_service
In the "Mach Services" section of the xpc_connection_create(3) man page, we have the following: Important: New service names may NOT be dynamically registered using xpc_connection_create_mach_service(). Only launchd jobs may listen on certain service names, and any service name that the job wishes to listen on must be declared in its launchd.plist(5). XPC may make allowances for dynamic name registration in debug scenarios, but these allowances abso- lutely will NOT be made in the production scenario. In a debugging scenario, how can I allow a dynamic name resolution for listeners? While the man page references this, it doesn't detail how to, and I can't find any information online about this. I can't tell if this text implies that it's currently possible, or may be allowed sometime in the future.
5
0
1.7k
Oct ’22
Get token audit for a NSXPCConnection
Hi, I have a question regarding securing XPC communication. I'm trying to get on the server side the process audit token for the connecting client. I've saw NSXPCConnection has a member called auditSessionIdentifier which I saw it is always returning same number for different connections. What does this represent, can it be used to identify the client connecting process? NSXPCConnection has auditToken, which is what I need, but it is a private property. I would use this, but I'm not sure if this will not result in app being rejected by Apple. Is anyone using it and had the app rejected/accepted? NSXPCConnection has processIdentifier but this alone it is kind of useless. But I was thinking to combine this with task_extmod_info (detect process changes) and audit token with task_name_for_pid. Any other suggestions to get the client process audit token based on NSXPCConnection? Thanks
5
0
2.9k
Oct ’22
Intermittently browser is getting disconnected with NetworkExtension
Hi, Greetings for the day! We would like to update you that we have created Content Filter NetworkExtension and this extension is working fine till Big Sur M1 however we are facing some strange problem in M1 Monterey. Intermittently, When we try to browse websites, it does not respond and after 3-5 minutes its opened the websites correctly. We would like to update you that our subclass overrides handleNewFlow, handleInboundDataFromFlow, handleOutboundDataFromFlow, handleInboundDataCompleteForFlow and handleOutboundDataCompleteForFlow. In all these methods we first check whether NEFilterFlow is nil or not and then pauseVerdict and once asynchronous methods completes execution then we call resumeFlow with verdict (allowVerdict/dropVerdict). When above mentioned issue generated we collected console streaming log and found these lines in the logs (Not from our application): Ignoring resume command for flow 3c8faf3c4a9f7 which does not exist Ignoring resume command for flow 3c90795d4d6f9 which does not exist Ignoring resume command for flow 3c9086d1ede69 which does not exist Ignoring resume command for flow 3c909b251d53b which does not exist We are not sure how above line get printed because we don’t have this logs in our source code so we would need your help to understand this problem and resolution so that we can solve this issue. We have couple of extra queries: What is flow mentioned in above logs in bold text? Is it NEFilterFlow's identifier or something else? How we can validate whether NEFilterFlow is valid or not before calling resumeFlow Why above line is getting printed in log which says flow does not exist. Is there any timeout maintained by NetworkExtension? We are using XPC for interprocess communication so our question is that, Is NetworkExtension/XPC maintain the queue size and if it overflow the size then above line is getting printed. If this is the case then how we can handle that? Is it known issue in NetworkExtension framework itself on M1 Monterey? Thanks & Regards, Mohmad Vasim
13
0
2.8k
Sep ’22
What is a Mach Service?
I've seen the term "Mach Service" used in many places. One such place being the man page for launchd.plist, another being xpc_connection_create_mach_service, yet I cannot find any documentation online explicitly defining what a Mach Service is. Closest thing I've found is the Mach Overview documentation, but that seems to be unrelated to "Mach Services" and closer to a more abstract umbrella term for a list of kernel primitives. So what is a Mach Service and what is the Mach bootstrap namespace? What functionality is it capable of? What is its purpose within an Application Bundle?
2
0
3.5k
Sep ’22
HmCharacteristic.readValue always crashes with bundleID is invalid and XPC not entitled
I want to print out every hmCharacteristic's value in my Smart Home and therefore created this screen (code see below). Unfortunately, I am always getting these two errors whenever I try to call the HmCharacteristic.readValue() function (error messages see below). I am using a physical test iPhone with a real HomeKit Smart Home (no HomeKitAccessorySimulator). I am also using a paid apple developer Account and did enable the HomeKit entitlement as well as allowed the app to use HomeKit data on my test iPhone. My question is: Why do I get the bundleId is invalid and XPC not entitled errors and how do I fix them? import SwiftUI import HomeKit struct screen: View { var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundColor(.accentColor) Text("Hello, world!") } .padding() } init() { let hmHomeManager = HMHomeManager(); DispatchQueue.main.asyncAfter(deadline: .now() + 1) { for hmHome in hmHomeManager.homes { for hmRoom in hmHome.rooms { for hmAccessory in hmRoom.accessories { for hmService in hmAccessory.services { for hmCharacteristic in hmService.characteristics { Task { hmCharacteristic.readValue { error in print("\(hmHome.name)/\(hmRoom.name)/\(hmAccessory.name)/\(hmService.name)/\(hmCharacteristic.localizedDescription) = \(hmCharacteristic.value ?? "[[nil]]")") } } } } } } } }; } } struct ContentView_Previews: PreviewProvider { static var previews: some View { screen() } } validateSessionInfo: bundleID is invalid. Please specify the bundleID for kRTCReportingSessionInfoClientBundleID initWithSessionInfo: XPC not entitled, 1
0
0
727
Sep ’22
[iOS 16 Crash] Crash while getting mach port from CFMessagePortRef
I create a local CFMessagePortRef using CFMessagePortCreateLocal and then use CFMachPortGetPort() to try to get the mach port from it like below: NSString *portIdentifier = [[groupName stringByAppendingString:@"."] stringByAppendingString:sdkId]; NSString *portName = [[portIdentifier stringByAppendingString:@"."] stringByAppendingString:@"mach.port"]; CFMessagePortContext context = {0,(__bridge void *)self,nil,nil,nil}; self.sendPort = CFMessagePortCreateLocal(kCFAllocatorDefault, (__bridge CFStringRef)portName, &callback, &context, false); CFMachPortGetPort(ms->_port); It works till iOS 15 but crashes on iOS 16. Can anyone help? I have defined below definition of __CFMessagePort: struct __CFMessagePort {     CFRuntimeBase _base;     CFLock_t _lock;     CFStringRef _name;     CFMachPortRef _port;        /* immutable; invalidated */     CFMutableDictionaryRef _replies;     int32_t _convCounter;     int32_t _perPID;            /* zero if not per-pid, else pid */     CFMachPortRef _replyPort;        /* only used by remote port; immutable once created; invalidated */     CFRunLoopSourceRef _source;        /* only used by local port; immutable once created; invalidated */     dispatch_source_t _dispatchSource;  /* only used by local port; invalidated */     dispatch_queue_t _dispatchQ;    /* only used by local port */     CFMessagePortInvalidationCallBack _icallout;     CFMessagePortCallBack _callout;    /* only used by local port; immutable */     CFMessagePortCallBackEx _calloutEx;    /* only used by local port; immutable */     CFMessagePortContext _context;    /* not part of remote port; immutable; invalidated */ };
4
0
1.9k
Sep ’22
XPC and App-to-App Communication
I’ve explained this issue many times before, both here on DevForums and in DTS tech support incidents, but never in a coherent fashion. This week I received yet another DTS TSI about this issue, and I’m using that as an excuse to write it up properly (-: Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" XPC and App-to-App Communication There is no supported way to directly communicate between apps using XPC. In the beginning… … there was Mach messaging. In Mach messaging, services are represented by a port, a kernel object that manages message-based IPC. A server has a receive right for a port, allowing it to receive messages that were sent to that port. A client that wants to send a message to a port must have a send right for that port. Mach is a capability-based system. You can’t create a send right from scratch; you must be granted it by someone. Mach messages can transfer send rights from process to process. However, this presents a chicken and egg problem: How do you get your first send right? The answer here is the bootstrap service. Every process starts with a send right to a bootstrap port. When a process wants to access a service, it sends a message with the service name to the bootstrap port. On success, the bootstrap service replies with a message with a send right to the port for that service. Not all processes talk to the same bootstrap port. Rather, the system manages multiple bootstrap ports, where each port represents a bootstrap namespace. The system gives each process a send right to the bootstrap port that’s appropriate for its execution context. The bootstrap service uses this bootstrap port to determine what service names are accessible to a client talking to that port. These bootstrap namespaces form a tree. At the root there is a global bootstrap namespace. A launchd daemon runs in that namespace. Below that are a set of per-user bootstrap namespaces, and below those are per-session bootstrap namespaces for each login sessions. A GUI app runs in a login session namespace. For a more in-depth explanation of this concept, see the Execution Contexts section of Technote 2083 Daemons and Agents. IMPORTANT That technote is very old and the bootstrap namespace model is now significantly more complex than what’s described there. However, the basic ideas are still valid. XPC Fundamentals XPC wraps Mach messaging in an API that’s much easier to use. An XPC connection represents a communication channel between two processes. An XPC listener listens for incoming connections. While there are anonymous listeners, most listeners are associated with a named endpoint, where the name is registered in a bootstrap namespace. This is what allows the client to connect to the listener by name. XPC is tightly integrated with the on-demand architecture supported by launchd. launchd manages a set of jobs — XPC services, launchd daemons and agents, and so on — and each job publishes a set of named endpoints. Client processes connect to these endpoints by name. Under the covers, XPC looks up the name in the client’s bootstrap namespace. A launchd job doesn’t need to run to publish its named endpoints. Rather, launchd learns about the endpoints by reading a property list associated with the job. For example: An XPC service advertises a single named endpoint, namely the bundle ID in the service’s Info.plist; for the details, see the xpcservice.plist man page. A launchd daemon can advertise multiple named endpoints via the MachServices property in its launchd property list; for the details, see the launchd.plist man page. launchd monitors these named endpoints for demand. When a client process sends a message to a connection that targets a named endpoint, launchd starts the associated job. The job then services the demand by starting XPC listeners for its named endpoints. XPC has two APIs: The low-level C API The Foundation XPC API, commonly referred to by the main class name, NSXPCConnection This post focuses on the latter but the same concepts apply to both. App-to-App Problems The XPC architecture is incompatible with direct app-to-app communication: There’s no way for launchd to know what named endpoints it should monitor on your app’s behalf. Launching an app is a heavyweight operation, one clearly visible to the user, so it’s not something that launchd can do on demand. This limitation is reflected in the XPC API. Specifically, there are three ways to create an XPC listener: The service() class method — This creates a listener for an XPC service’s named endpoint. The init(machServiceName:) initialiser — This creates a listener for one of the names advertised in the MachServices property of a launchd daemon or agent. The anonymous() class method — This creates an anonymous listener. None of these are useful in setting up app-to-app communication. The Xcode Gotcha One particularly gnarly gotcha here is that app-to-app communication using XPC works when you run your apps from Xcode. This is a side effect of the infrastructure used by Xcode to debug XPC services. That infrastructure allows the listener app to create a listener using init(machServiceName:) even though the corresponding service name is not known to launchd. So your code works in the debugger but then fails when you run it from the Finder. Ouch! Alternatives If you can’t use XPC for app-to-app communication, what are the alternatives? Here’s a short list of things that might work: Unix domain sockets — For the details, see the unix man page, or any good text book an BSD Sockets. CFMessagePort — For the details, see its documentation. XPC rendezvous — See the XPC Rendezvous section, below. Which is best depends on your circumstances. Unix domain sockets is an industry standard API that works well. It relies on the BSD Sockets API, which is un-fun to call from Swift. Its access control is based on file system permissions, which is helpful if you need to cut across bootstrap namespaces. In contrast, CFMessagePort is a thin wrapper around Mach messaging. That means that its tied to your bootstrap namespace, which can be useful. It’s relatively easy to call from Swift, but still not trivial. XPC rendezvous is based on XPC, so it has all of its advantages. The main disadvantages is that it requires a launchd job to help with the rendezvous, which isn’t always feasible. Oh, and here’s a short list of things to avoid: Mach messaging — I strongly recommend against using Mach messaging directly. It’s almost impossible to use correctly. Distributed Objects (DO) — This has been deprecated for many years now, and for good reason. It has a wide range of weird and wonderful bugs. XPC Rendezvous One way to set up app-to-app communication is with an XPC rendezvous. This technique requires a launchd job that’s visible to both parties: This launchd job advertises a named endpoint. Client A calls the anonymous() class method to create an anonymous listener. It then uses the endpoint property to get an endpoint (NSXPCListenerEndpoint) for that listener. It uses XPC to send this endpoint to the launchd job. The launchd job stores this endpoint. Client B uses XPC to get the endpoint from the launchd job. Client B passes the endpoint to the init(listenerEndpoint:) initialiser to open a connection directly to client A. IMPORTANT The launchd job in step one cannot be an XPC service. Third-party XPC services are always scoped to their container app (see the discussion of the ServiceType property in the xpcservice.plist man page) and thus can’t fulfil the primary requirement of an XPC rendezvous, namely, to be visible to both parties. Most other launchd jobs do work for this, including: launchd daemons and agents Service Management login items System extensions
0
0
5.9k
Sep ’22
XPC execute response block when one side died
Hi, I have a problem with XPC communication, maybe someone has a suggestion how to fix it. So I have 2 applications that communicate over XPC (NSXPCConnection). One app (sender) calls a method that ends up on the other side(receiver). The method has a completion block to get the response back. The problem is that the receiver crashes while executing the method, before sending back a response. The invalidationHandler is called, because the connection died. My question is: is there a way to make XPC execute the response block, with error or something? If not, any suggestions how to handle this case, to "fake" call the response block for sender? Thanks
3
0
1.7k
Sep ’22
XPC: Can an NSXPCInterface have a throwing method?
I can't seem to find any references to this in the docs, which is odd because it seems like it would be a fairly common consideration. When defining an NSXPCInterface for an object to be proxied, can XPC support methods that throw exceptions - coding and passing those exceptions back to the peer? Given NSError does not implement NSSecureCoding, I can't see how it would work. But perhaps there is some other approach I don't know about. And assuming there is no support for propagating exceptions across XPC, how does one typically handle the need to return errors from XPC interfaces?
1
0
1.1k
Aug ’22
FileProvider: How to do IPC with a FileProviderExtension
I am attempting to communicate with a FileProviderExtension (NSFileProviderReplicatedExtension) using XPC. I want to allow the controlling application to manage how the extension communicates with the remote service - e.g. tell the extension to sign in (and provide credentials to do so), sign out, etc. I have implemented the NSFileProviderServicing protocol in the extension and provided an NSFileProviderServiceSource (following the FruitBasket sample code). However, I can't see how to get a general connection to the service from the controlling application. The only method I can find is FileManager().fileProviderServicesForItem(at url: URL), which requires a URL for an item managed by the extension. Given the controlling application wants to perform actions like sign in, there is no item in scope and thus no URL. I tried using NSFileProviderManager.documentStorageURL, but this is not available on macos. Any idea how to get the connection without a file URL? Or how to get a general purpose URL that will suffice? Or is there another IPC mechanism I should use?
1
0
1.1k
Aug ’22
IPC connection failed to reconnect after system network extension replacement
I have a system network extension that is installed by my app. When I update my app I also update me system extension by returning ReplacementAction.replace at the actionForReplacingExtension delegate. When the new extension starts, I created a new NSXPCListener with the same mach service name, but the app can’t register to it. I updated from MyExtension version 1 to MyExtension version 2. On the Console logs I see: launchd: [system:] Service "NetworkExtension.com.MyExtension.2" tried to register for endpoint "machServiceName" already registered by owner: NetworkExtension.MyExtension.1 launchd: [system:] failed activation: name = machServiceName, flags = 0x0, requestor = MyApp[38340], error = 1: Operation not permitted
8
0
2.3k
Aug ’22
Launching an executable from an extension process
I'm building ExtensionKit support into my application, which is currently not sandboxed. The extensions must be sandboxed. For my use case, I need them to be able to launch executables the user has installed, with homebrew for example. My problem is some paths appear to disallow execution. As an example, an extension may want to run "/opt/homebrew/bin/go". This is actually just a symlink that ultimately resolves to another path under "/opt/homebrew". If I pass along a non-security-scoped bookmark to the extension process, it is able to read the files under "/opt/homebrew". But, it cannot execute anything. The only way I have found to enable execution is by setting "com.apple.security.temporary-exception.files.absolute-path.read-only" to ["/opt/homebrew/"]. Yet, I feel like there must be a way to do this. BBEdit has a feature that allows the user to type an executable path in its settings. It will then be able to launch the targeted process, despite being a sandboxed app. Am I seeing an ExtensionKit-specific limitation? Or perhaps my extension needs additional entitlements?
2
0
1.6k
Jul ’22
Can NSXPCInterface work with non-void return type?
Can NSXPCInterface work with non-void return type? The official document says, "All messages must be 'void' return type." (Reference) However, in the file provider sample code of wwdc21, it uses it with non-void return type. (Reference) I wondered whether it can or can not be used with non-void return type? Is the answer changed after swift support for async/await?
0
0
1.6k
Jul ’22
Notification on NSUserDefaults Change When App Is In Background
I have two applications that share an app group @"group.edu.tds.poc.shared"Using NSUserDefaults of this app group, I am able to exchange data between the reader app and the writer app.The Writer App writes data to the NSUserDefaultNSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.edu.tds.poc.shared"]; [share dDefaults setObject:stringToStore forKey:key]; [sharedDefaults synchronize];The Reader App reads the data from the NSUserDefaultNSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.edu.tds.poc.shared"]; return [sharedDefaults stringForKey:@"key"];I now want the reader app to be notified as soon as the writer app modifies the value for the key. I.e. Can I notify the reader app when the writer app is running in the foreground (Reader app is either running in the background or is not running).I have implemented the marked solution but with no luck:Cocoa - Notification on NSUserDefaults value change?NSUserDefaults and KVO issuesCall back only when the Reader App is in foreground state. (Wrote to the Shared Defaults using Reader app)Any idea if this can be achieved?
Replies
6
Boosts
1
Views
3.1k
Activity
Nov ’22
Communicating between System Extension (specifically, camera extension) and Container App
i am trying to send data from the container app to its system extension (specifically, a camera extension. i am not entirely sure how to go about this. i read that i could utilize XPC, and i have tried this but for some reason, the system extension returns nothing when trying to connect to the XPC Service. below is an example code snipper let connectionToService = NSXPCConnection(serviceName: "example.VirtualCameraXPC")     connectionToService.remoteObjectInterface = NSXPCInterface(with:VirtualCameraXPCProtocol.self)     connectionToService.resume() //checking processes available shows that the XPC Server process is never activated also. if let proxy = connectionToService.remoteObjectProxy as? VirtualCameraXPCProtocol {       proxy.getNewString() { aString in //this is never logged.         NSLog("Second result string was: \(aString)")       }     } when i run the above code from the container app, it works properly but from the system extension, i run into the problems commented above in the code block. i would appreciate any help that will help me successfully send data from my container app to my camera extension.
Replies
1
Boosts
0
Views
1.2k
Activity
Nov ’22
Communication between system extension and the app
I have a working NEPacketTunnelProvider app extension macOS app on the App Store. The company wants to explore the possibility of switching to system extension, so that we can distribute the app outside of the appstore too. I managed to do the switch and the extension works. But the communication is broken. DistributedNotificationCenter stopped working for me after switching to system extension, events are not received and I don't see any errors so I cannot say what's wrong. I tried to adopt XPC from this Filtering Network Traffic Apple's sample, but I get sandbox error - domain code 4099, failed at lookup with error 159 - Sandbox restriction. I get the same error if I try to run the sample with my company team id. I do these changes: NEMachServiceName to $(TeamIdentifierPrefix)com.mycompanyname.macos.dev App Groups to $(TeamIdentifierPrefix)com.mycompanyname.macos.dev Bundle ids to com.mycompanyname.macos.dev and com.mycompanyname.macos.dev.tunnelprovider com.mycompanyname.macos.dev has capabilities - App Groups, Network Extensions, System Extensions com.mycompanyname.macos.dev.tunnelprovider - Network Extensions, System Extensions Could you help me find the reason why DistributedNotificationCenter could stop receiving notifications? Or are you able to run Apple's sample? What changes do you make to run it under your team? Because it looks like my changes are wrong Either DistributedNotificationCenter or XPC would solve my problem
Replies
3
Boosts
1
Views
1.4k
Activity
Nov ’22
XPC Connection Not Invalidated on NSXPCListenerDelegate Denial
I am attempting to understand the expected behavior of several points related to XPC. Mainly, I have the following questions: Should I expect that NSXPCConnection.remoteObjectProxyWithErrorHandler to call the error handler if the associated NSXPCListenerDelegate returns false in its listener function? Does the error handler get called immediately if the remoteObjectProxyWithErrorHandler function fails? What does remoteObjectProxy return if an actual proxy object is never exported on the service-side (in the listener function)? Should I expect that NSXPCConnection.invalidate() and/or the connection's invalidationHandler to be called when the associated NSXPCListenerDelegate returns false in its listener function? According to the listener documentation below, it appears the listener function is supposed to invalidate the connection; however, I am not seeing this be the case. https://developer.apple.com/documentation/foundation/nsxpclistenerdelegate/1410381-listener To reject the connect, return a value of false. This causes the connection object to be invalidated. I have written a test that exhibits the fact when the listener function returns false, rather than the invalidationHandler being called, the interruptionHandler is called. import XCTest final class InvalidateTest: XCTestCase {   func testConnectionIsInvalidatedOnListenerRejection() {     //set up anonymous XPC Listener     let listener = NSXPCListener.anonymous()     let listenerDelegate = MockListenerDelegate()     listener.delegate = listenerDelegate     listener.resume()           //establish connection to service     let clientConnection = MockConnection(listenerEndpoint: listener.endpoint)     var interruptHandlerCalled = false     var invalidateHandlerCalled = false     let interruptionHandler = { interruptHandlerCalled = true }     let invalidationHandler = { invalidateHandlerCalled = true }     clientConnection.interruptionHandler = interruptionHandler     clientConnection.invalidationHandler = invalidationHandler     clientConnection.remoteObjectInterface = NSXPCInterface(with: XPCServDelegate.self)     clientConnection.exportedInterface = NSXPCInterface(with: XPCCliDelegate.self)     clientConnection.exportedObject = XPCCDelegate()     clientConnection.resume()           // get the proxy delegate to make the XPC pulse call     guard let proxy = clientConnection.remoteObjectProxy() as? XPCServDelegate else {       XCTAssert(false, "Unable to get proxy object")       return     }     // why is it not failing to get a proxy object in this case?           // make the pulse call     var replyCalled = false     let semaphore = DispatchSemaphore(value: 0)     proxy.pulse(reply: {       replyCalled = true       semaphore.signal()     })           let waitResult = semaphore.wait(timeout: .now() + 1)     XCTAssertEqual(waitResult, .timedOut)     XCTAssertFalse(replyCalled)           // why do these assertions fail?     XCTAssertTrue(clientConnection.invalidateCalled)     XCTAssertTrue(invalidateHandlerCalled)     XCTAssertFalse(interruptHandlerCalled)   } } @objc public protocol XPCServDelegate {   func pulse(reply: @escaping () -> Void) } class XPCSDelegate : XPCServDelegate {   func pulse(reply: @escaping () -> Void) {     reply()   } } @objc public protocol XPCCliDelegate {} class XPCCDelegate : XPCCliDelegate {} fileprivate class MockConnection : NSXPCConnection {   var invalidateCalled = false       override func invalidate() {     invalidateCalled = true     super.invalidate()   } } fileprivate class MockListenerDelegate : NSObject, NSXPCListenerDelegate {   func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {     return false   } }
Replies
0
Boosts
0
Views
1.8k
Activity
Nov ’22
XPC between Container App and System Extension
I'm trying to create an XPC service to communicate between my Endpoint Security Extension and its Container App. I've taken the Sample Endpoint App from here. I've then followed the steps under Creating the Service here In fact, when I added the XPC service via the template, Xcode automatically added an Embed XPC Services phase to the container app. I can confirm that in the built container app I see the xpc service: SampleEndpointApp.app/Contents/XPCServices/Service.xpc If I initiate an NSXPCConnection from the container app then I can both connect and make RPCs. Furthermore I see the service process running via ps and also launchtl. If however I try to initiate an NSXPCConnection from the extension then I see nothing. RPC doesn't work and I don't see the service being launched. I've tried this with and without the connection in the main app. What am I missing here? What needs to be done to allow both processes to talk to each other? Is there some permissions issue here? Note that my plist for the service is as follows:
Replies
13
Boosts
0
Views
3.7k
Activity
Oct ’22
How to publish an XPC Service in a global daemon that employs EndpointSecurity framework?
I have a global daemon managed by launchd, whose .plist is installed in /Library/LaunchDaemons). To be correctly entitled and code-signed so it can communicate with EndpointSecurity framework, its executable resides in a normal Mac App bundle (main() will run as minimal UI when launched from UI, and as a daemon when launched by launchd). This means that the ProgramArguments.0 in its .plist looks something like /Library/PrivilegedHelperTools/MyDaemonApp.app/Contents/MacOS/MyDaemonApp Now I need this daemon to publish an XPC Service (with few control commands) so that other components of our system (UI app, a user-context launchd-daemon and another launchd global-daemon) will be able to connect to The XPC Service and control it via the published protocol. I read some answers here, and also found a working order sample code that does just this here - https://github.com/jdspoone/SampleOSXLaunchDaemon But when I apply its content to my global daemon, word for word - it doesn't work - meaning, clients cannot create a connection to The XPC Service. The daemon is up and running, and functional. its .plist is quite simple and looks like this: <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.mycompany.itm.service</string> <key>KeepAlive</key> <true/> <key>RunAtLoad</key> <true/> <key>MachServices</key> <dict> <key>com.mycompany.itm.service</key> <true/> </dict> <key>ProgramArguments</key> <array> <string>/Library/PrivilegedHelperTools/IMyDaemonApp.app/Contents/MacOS/MyDaemonApp</string> <string>-monitor</string> <string>-protectDeviceProtocol</string> <string>USB</string> </array> </dict> </plist> It creates and starts an XPC listener in MYXPCListener.h like thus: #import <Foundation/Foundation.h> #import "MYXPCProtocol.h" NS_ASSUME_NONNULL_BEGIN @interface OITPreventionXPCService : NSObject (instancetype) init; (void) start; /* Begin listening for incoming XPC connections */ (void) stop; /* Stop listening for incoming XPC connections */ @end NS_ASSUME_NONNULL_END and the implementation is: /* AppDelegate.m */ @interface MYXPCService () <NSXPCListenerDelegate, OITPreventionXPCProtocol> @property (nonatomic, strong, readwrite) NSXPCListener *listener; @property (nonatomic, readwrite) BOOL started; @end @implementation OITPreventionXPCService (instancetype) init {     if ((self = [super init]) != nil) {         _listener = [[NSXPCListener alloc] initWithMachServiceName:@"com.mycompany.itm.service"];         _listener.delegate = self;         if (_listener == nil) {             os_log_error(myLog, "XPCListener failed to initialize");         }         _started = NO;     }     return self; } (void) start {     assert(_started == NO);     [_listener resume];     os_log_info(myLog, "XPCListener resumed");     _started = YES; } (void) stop {     assert(_started == YES);     [_listener suspend];     os_log_info(myLog, "XPCListener suspended");     _started = NO; } /* NSXPCListenerDelegate implementation */ (BOOL) listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {     os_log_info(myLog, "Prevention XPCListener is bequsted a new connection");     assert(listener == _listener);     assert(newConnection != nil);     newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MYXPCProtocol)];     newConnection.exportedObject = self; &#9;&#9;[newConnection resume];     return YES; } /* Further down this implementation, I have implementations to all the methods in MYXPCProtocol. */ @end Now the client code (and I tried EVERY kind of client, signed unsigned, daemon, UI, root privileged, or user-scoped - whatever). For example, in the AppDelegate of a UI app: #import "AppDelegate.h" #import "MYXPCProtocol.h" @interface AppDelegate () @property (strong) IBOutlet NSWindow *window; @property (nonatomic, strong, readwrite) NSXPCConnection *connection; /* lazy initialized */ @end @implementation AppDelegate (NSXPCConnection *) connection {     if (_connection == nil) {         _connection = [[NSXPCConnection alloc] initWithMachServiceName:daemonLabel options:NSXPCConnectionPrivileged];         _connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MYXPCProtocol)];         _connection.invalidationHandler =  ^{             self->_connection = nil;             NSLog(@"connection has been invalidated");         };         [_connection resume];         /* New connections always start suspended */     }     return _connection; } (IBAction) getServiceStatus:(id)sender {     [self.connection.remoteObjectProxy getStatus:^(NSString * _Nonnull status) {         NSLog(@"MY XPC Service status is: %@", status);     }]; } @end but no matter what I do - I always get the "connection invalidated". The sample launchDaemon that works - is not code-signed at all!!! but mine, which is both signed and checking of which yields $ spctl --assess --verbose IMyDaemonApp.app IMyDaemonApp.app: accepted source=Notarized Developer ID   I'm at a loss - and would like to get any advice, or any helpful documentation (I've been reading TN2083, and man launchctl and man launchd.plist and many other pages - to no avail. There seems to be no real "programming guide" for XPC and no reasonable sample code on Apple developer site to fit my needs. Last - this is MacOS 10.15.7, and latest Xcode 12.3
Replies
7
Boosts
0
Views
3.0k
Activity
Oct ’22
TN3113: Testing and debugging XPC code with an anonymous listener
Use an anonymous XPC listener to simplify your XPC testing and debugging. View Technote TN3113 &amp;gt;
Replies
0
Boosts
0
Views
1.2k
Activity
Oct ’22
Allowing dynamic name registration in xpc_connection_create_mach_service
In the "Mach Services" section of the xpc_connection_create(3) man page, we have the following: Important: New service names may NOT be dynamically registered using xpc_connection_create_mach_service(). Only launchd jobs may listen on certain service names, and any service name that the job wishes to listen on must be declared in its launchd.plist(5). XPC may make allowances for dynamic name registration in debug scenarios, but these allowances abso- lutely will NOT be made in the production scenario. In a debugging scenario, how can I allow a dynamic name resolution for listeners? While the man page references this, it doesn't detail how to, and I can't find any information online about this. I can't tell if this text implies that it's currently possible, or may be allowed sometime in the future.
Replies
5
Boosts
0
Views
1.7k
Activity
Oct ’22
Get token audit for a NSXPCConnection
Hi, I have a question regarding securing XPC communication. I'm trying to get on the server side the process audit token for the connecting client. I've saw NSXPCConnection has a member called auditSessionIdentifier which I saw it is always returning same number for different connections. What does this represent, can it be used to identify the client connecting process? NSXPCConnection has auditToken, which is what I need, but it is a private property. I would use this, but I'm not sure if this will not result in app being rejected by Apple. Is anyone using it and had the app rejected/accepted? NSXPCConnection has processIdentifier but this alone it is kind of useless. But I was thinking to combine this with task_extmod_info (detect process changes) and audit token with task_name_for_pid. Any other suggestions to get the client process audit token based on NSXPCConnection? Thanks
Replies
5
Boosts
0
Views
2.9k
Activity
Oct ’22
Intermittently browser is getting disconnected with NetworkExtension
Hi, Greetings for the day! We would like to update you that we have created Content Filter NetworkExtension and this extension is working fine till Big Sur M1 however we are facing some strange problem in M1 Monterey. Intermittently, When we try to browse websites, it does not respond and after 3-5 minutes its opened the websites correctly. We would like to update you that our subclass overrides handleNewFlow, handleInboundDataFromFlow, handleOutboundDataFromFlow, handleInboundDataCompleteForFlow and handleOutboundDataCompleteForFlow. In all these methods we first check whether NEFilterFlow is nil or not and then pauseVerdict and once asynchronous methods completes execution then we call resumeFlow with verdict (allowVerdict/dropVerdict). When above mentioned issue generated we collected console streaming log and found these lines in the logs (Not from our application): Ignoring resume command for flow 3c8faf3c4a9f7 which does not exist Ignoring resume command for flow 3c90795d4d6f9 which does not exist Ignoring resume command for flow 3c9086d1ede69 which does not exist Ignoring resume command for flow 3c909b251d53b which does not exist We are not sure how above line get printed because we don’t have this logs in our source code so we would need your help to understand this problem and resolution so that we can solve this issue. We have couple of extra queries: What is flow mentioned in above logs in bold text? Is it NEFilterFlow's identifier or something else? How we can validate whether NEFilterFlow is valid or not before calling resumeFlow Why above line is getting printed in log which says flow does not exist. Is there any timeout maintained by NetworkExtension? We are using XPC for interprocess communication so our question is that, Is NetworkExtension/XPC maintain the queue size and if it overflow the size then above line is getting printed. If this is the case then how we can handle that? Is it known issue in NetworkExtension framework itself on M1 Monterey? Thanks & Regards, Mohmad Vasim
Replies
13
Boosts
0
Views
2.8k
Activity
Sep ’22
What is a Mach Service?
I've seen the term "Mach Service" used in many places. One such place being the man page for launchd.plist, another being xpc_connection_create_mach_service, yet I cannot find any documentation online explicitly defining what a Mach Service is. Closest thing I've found is the Mach Overview documentation, but that seems to be unrelated to "Mach Services" and closer to a more abstract umbrella term for a list of kernel primitives. So what is a Mach Service and what is the Mach bootstrap namespace? What functionality is it capable of? What is its purpose within an Application Bundle?
Replies
2
Boosts
0
Views
3.5k
Activity
Sep ’22
HmCharacteristic.readValue always crashes with bundleID is invalid and XPC not entitled
I want to print out every hmCharacteristic's value in my Smart Home and therefore created this screen (code see below). Unfortunately, I am always getting these two errors whenever I try to call the HmCharacteristic.readValue() function (error messages see below). I am using a physical test iPhone with a real HomeKit Smart Home (no HomeKitAccessorySimulator). I am also using a paid apple developer Account and did enable the HomeKit entitlement as well as allowed the app to use HomeKit data on my test iPhone. My question is: Why do I get the bundleId is invalid and XPC not entitled errors and how do I fix them? import SwiftUI import HomeKit struct screen: View { var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundColor(.accentColor) Text("Hello, world!") } .padding() } init() { let hmHomeManager = HMHomeManager(); DispatchQueue.main.asyncAfter(deadline: .now() + 1) { for hmHome in hmHomeManager.homes { for hmRoom in hmHome.rooms { for hmAccessory in hmRoom.accessories { for hmService in hmAccessory.services { for hmCharacteristic in hmService.characteristics { Task { hmCharacteristic.readValue { error in print("\(hmHome.name)/\(hmRoom.name)/\(hmAccessory.name)/\(hmService.name)/\(hmCharacteristic.localizedDescription) = \(hmCharacteristic.value ?? "[[nil]]")") } } } } } } } }; } } struct ContentView_Previews: PreviewProvider { static var previews: some View { screen() } } validateSessionInfo: bundleID is invalid. Please specify the bundleID for kRTCReportingSessionInfoClientBundleID initWithSessionInfo: XPC not entitled, 1
Replies
0
Boosts
0
Views
727
Activity
Sep ’22
[iOS 16 Crash] Crash while getting mach port from CFMessagePortRef
I create a local CFMessagePortRef using CFMessagePortCreateLocal and then use CFMachPortGetPort() to try to get the mach port from it like below: NSString *portIdentifier = [[groupName stringByAppendingString:@"."] stringByAppendingString:sdkId]; NSString *portName = [[portIdentifier stringByAppendingString:@"."] stringByAppendingString:@"mach.port"]; CFMessagePortContext context = {0,(__bridge void *)self,nil,nil,nil}; self.sendPort = CFMessagePortCreateLocal(kCFAllocatorDefault, (__bridge CFStringRef)portName, &callback, &context, false); CFMachPortGetPort(ms->_port); It works till iOS 15 but crashes on iOS 16. Can anyone help? I have defined below definition of __CFMessagePort: struct __CFMessagePort {     CFRuntimeBase _base;     CFLock_t _lock;     CFStringRef _name;     CFMachPortRef _port;        /* immutable; invalidated */     CFMutableDictionaryRef _replies;     int32_t _convCounter;     int32_t _perPID;            /* zero if not per-pid, else pid */     CFMachPortRef _replyPort;        /* only used by remote port; immutable once created; invalidated */     CFRunLoopSourceRef _source;        /* only used by local port; immutable once created; invalidated */     dispatch_source_t _dispatchSource;  /* only used by local port; invalidated */     dispatch_queue_t _dispatchQ;    /* only used by local port */     CFMessagePortInvalidationCallBack _icallout;     CFMessagePortCallBack _callout;    /* only used by local port; immutable */     CFMessagePortCallBackEx _calloutEx;    /* only used by local port; immutable */     CFMessagePortContext _context;    /* not part of remote port; immutable; invalidated */ };
Replies
4
Boosts
0
Views
1.9k
Activity
Sep ’22
XPC and App-to-App Communication
I’ve explained this issue many times before, both here on DevForums and in DTS tech support incidents, but never in a coherent fashion. This week I received yet another DTS TSI about this issue, and I’m using that as an excuse to write it up properly (-: Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" XPC and App-to-App Communication There is no supported way to directly communicate between apps using XPC. In the beginning… … there was Mach messaging. In Mach messaging, services are represented by a port, a kernel object that manages message-based IPC. A server has a receive right for a port, allowing it to receive messages that were sent to that port. A client that wants to send a message to a port must have a send right for that port. Mach is a capability-based system. You can’t create a send right from scratch; you must be granted it by someone. Mach messages can transfer send rights from process to process. However, this presents a chicken and egg problem: How do you get your first send right? The answer here is the bootstrap service. Every process starts with a send right to a bootstrap port. When a process wants to access a service, it sends a message with the service name to the bootstrap port. On success, the bootstrap service replies with a message with a send right to the port for that service. Not all processes talk to the same bootstrap port. Rather, the system manages multiple bootstrap ports, where each port represents a bootstrap namespace. The system gives each process a send right to the bootstrap port that’s appropriate for its execution context. The bootstrap service uses this bootstrap port to determine what service names are accessible to a client talking to that port. These bootstrap namespaces form a tree. At the root there is a global bootstrap namespace. A launchd daemon runs in that namespace. Below that are a set of per-user bootstrap namespaces, and below those are per-session bootstrap namespaces for each login sessions. A GUI app runs in a login session namespace. For a more in-depth explanation of this concept, see the Execution Contexts section of Technote 2083 Daemons and Agents. IMPORTANT That technote is very old and the bootstrap namespace model is now significantly more complex than what’s described there. However, the basic ideas are still valid. XPC Fundamentals XPC wraps Mach messaging in an API that’s much easier to use. An XPC connection represents a communication channel between two processes. An XPC listener listens for incoming connections. While there are anonymous listeners, most listeners are associated with a named endpoint, where the name is registered in a bootstrap namespace. This is what allows the client to connect to the listener by name. XPC is tightly integrated with the on-demand architecture supported by launchd. launchd manages a set of jobs — XPC services, launchd daemons and agents, and so on — and each job publishes a set of named endpoints. Client processes connect to these endpoints by name. Under the covers, XPC looks up the name in the client’s bootstrap namespace. A launchd job doesn’t need to run to publish its named endpoints. Rather, launchd learns about the endpoints by reading a property list associated with the job. For example: An XPC service advertises a single named endpoint, namely the bundle ID in the service’s Info.plist; for the details, see the xpcservice.plist man page. A launchd daemon can advertise multiple named endpoints via the MachServices property in its launchd property list; for the details, see the launchd.plist man page. launchd monitors these named endpoints for demand. When a client process sends a message to a connection that targets a named endpoint, launchd starts the associated job. The job then services the demand by starting XPC listeners for its named endpoints. XPC has two APIs: The low-level C API The Foundation XPC API, commonly referred to by the main class name, NSXPCConnection This post focuses on the latter but the same concepts apply to both. App-to-App Problems The XPC architecture is incompatible with direct app-to-app communication: There’s no way for launchd to know what named endpoints it should monitor on your app’s behalf. Launching an app is a heavyweight operation, one clearly visible to the user, so it’s not something that launchd can do on demand. This limitation is reflected in the XPC API. Specifically, there are three ways to create an XPC listener: The service() class method — This creates a listener for an XPC service’s named endpoint. The init(machServiceName:) initialiser — This creates a listener for one of the names advertised in the MachServices property of a launchd daemon or agent. The anonymous() class method — This creates an anonymous listener. None of these are useful in setting up app-to-app communication. The Xcode Gotcha One particularly gnarly gotcha here is that app-to-app communication using XPC works when you run your apps from Xcode. This is a side effect of the infrastructure used by Xcode to debug XPC services. That infrastructure allows the listener app to create a listener using init(machServiceName:) even though the corresponding service name is not known to launchd. So your code works in the debugger but then fails when you run it from the Finder. Ouch! Alternatives If you can’t use XPC for app-to-app communication, what are the alternatives? Here’s a short list of things that might work: Unix domain sockets — For the details, see the unix man page, or any good text book an BSD Sockets. CFMessagePort — For the details, see its documentation. XPC rendezvous — See the XPC Rendezvous section, below. Which is best depends on your circumstances. Unix domain sockets is an industry standard API that works well. It relies on the BSD Sockets API, which is un-fun to call from Swift. Its access control is based on file system permissions, which is helpful if you need to cut across bootstrap namespaces. In contrast, CFMessagePort is a thin wrapper around Mach messaging. That means that its tied to your bootstrap namespace, which can be useful. It’s relatively easy to call from Swift, but still not trivial. XPC rendezvous is based on XPC, so it has all of its advantages. The main disadvantages is that it requires a launchd job to help with the rendezvous, which isn’t always feasible. Oh, and here’s a short list of things to avoid: Mach messaging — I strongly recommend against using Mach messaging directly. It’s almost impossible to use correctly. Distributed Objects (DO) — This has been deprecated for many years now, and for good reason. It has a wide range of weird and wonderful bugs. XPC Rendezvous One way to set up app-to-app communication is with an XPC rendezvous. This technique requires a launchd job that’s visible to both parties: This launchd job advertises a named endpoint. Client A calls the anonymous() class method to create an anonymous listener. It then uses the endpoint property to get an endpoint (NSXPCListenerEndpoint) for that listener. It uses XPC to send this endpoint to the launchd job. The launchd job stores this endpoint. Client B uses XPC to get the endpoint from the launchd job. Client B passes the endpoint to the init(listenerEndpoint:) initialiser to open a connection directly to client A. IMPORTANT The launchd job in step one cannot be an XPC service. Third-party XPC services are always scoped to their container app (see the discussion of the ServiceType property in the xpcservice.plist man page) and thus can’t fulfil the primary requirement of an XPC rendezvous, namely, to be visible to both parties. Most other launchd jobs do work for this, including: launchd daemons and agents Service Management login items System extensions
Replies
0
Boosts
0
Views
5.9k
Activity
Sep ’22
XPC execute response block when one side died
Hi, I have a problem with XPC communication, maybe someone has a suggestion how to fix it. So I have 2 applications that communicate over XPC (NSXPCConnection). One app (sender) calls a method that ends up on the other side(receiver). The method has a completion block to get the response back. The problem is that the receiver crashes while executing the method, before sending back a response. The invalidationHandler is called, because the connection died. My question is: is there a way to make XPC execute the response block, with error or something? If not, any suggestions how to handle this case, to "fake" call the response block for sender? Thanks
Replies
3
Boosts
0
Views
1.7k
Activity
Sep ’22
XPC: Can an NSXPCInterface have a throwing method?
I can't seem to find any references to this in the docs, which is odd because it seems like it would be a fairly common consideration. When defining an NSXPCInterface for an object to be proxied, can XPC support methods that throw exceptions - coding and passing those exceptions back to the peer? Given NSError does not implement NSSecureCoding, I can't see how it would work. But perhaps there is some other approach I don't know about. And assuming there is no support for propagating exceptions across XPC, how does one typically handle the need to return errors from XPC interfaces?
Replies
1
Boosts
0
Views
1.1k
Activity
Aug ’22
FileProvider: How to do IPC with a FileProviderExtension
I am attempting to communicate with a FileProviderExtension (NSFileProviderReplicatedExtension) using XPC. I want to allow the controlling application to manage how the extension communicates with the remote service - e.g. tell the extension to sign in (and provide credentials to do so), sign out, etc. I have implemented the NSFileProviderServicing protocol in the extension and provided an NSFileProviderServiceSource (following the FruitBasket sample code). However, I can't see how to get a general connection to the service from the controlling application. The only method I can find is FileManager().fileProviderServicesForItem(at url: URL), which requires a URL for an item managed by the extension. Given the controlling application wants to perform actions like sign in, there is no item in scope and thus no URL. I tried using NSFileProviderManager.documentStorageURL, but this is not available on macos. Any idea how to get the connection without a file URL? Or how to get a general purpose URL that will suffice? Or is there another IPC mechanism I should use?
Replies
1
Boosts
0
Views
1.1k
Activity
Aug ’22
IPC connection failed to reconnect after system network extension replacement
I have a system network extension that is installed by my app. When I update my app I also update me system extension by returning ReplacementAction.replace at the actionForReplacingExtension delegate. When the new extension starts, I created a new NSXPCListener with the same mach service name, but the app can’t register to it. I updated from MyExtension version 1 to MyExtension version 2. On the Console logs I see: launchd: [system:] Service "NetworkExtension.com.MyExtension.2" tried to register for endpoint "machServiceName" already registered by owner: NetworkExtension.MyExtension.1 launchd: [system:] failed activation: name = machServiceName, flags = 0x0, requestor = MyApp[38340], error = 1: Operation not permitted
Replies
8
Boosts
0
Views
2.3k
Activity
Aug ’22
Launching an executable from an extension process
I'm building ExtensionKit support into my application, which is currently not sandboxed. The extensions must be sandboxed. For my use case, I need them to be able to launch executables the user has installed, with homebrew for example. My problem is some paths appear to disallow execution. As an example, an extension may want to run "/opt/homebrew/bin/go". This is actually just a symlink that ultimately resolves to another path under "/opt/homebrew". If I pass along a non-security-scoped bookmark to the extension process, it is able to read the files under "/opt/homebrew". But, it cannot execute anything. The only way I have found to enable execution is by setting "com.apple.security.temporary-exception.files.absolute-path.read-only" to ["/opt/homebrew/"]. Yet, I feel like there must be a way to do this. BBEdit has a feature that allows the user to type an executable path in its settings. It will then be able to launch the targeted process, despite being a sandboxed app. Am I seeing an ExtensionKit-specific limitation? Or perhaps my extension needs additional entitlements?
Replies
2
Boosts
0
Views
1.6k
Activity
Jul ’22
Can NSXPCInterface work with non-void return type?
Can NSXPCInterface work with non-void return type? The official document says, "All messages must be 'void' return type." (Reference) However, in the file provider sample code of wwdc21, it uses it with non-void return type. (Reference) I wondered whether it can or can not be used with non-void return type? Is the answer changed after swift support for async/await?
Replies
0
Boosts
0
Views
1.6k
Activity
Jul ’22