Posts

Post not yet marked as solved
0 Replies
90 Views
I have a two-level view-based NSOutlineView, employing a data-source and delegate. It is not populated by binding. My code is all ObjC, and my problem persist since MacOS 10.11 to the current MacOS 14.4.x Because of some specific visual issue, I use a very simple subclass of NSTableRowView, which has only this: @implementation MYRowView - (void)drawRect:(NSRect)dirtyRect { self.emphasized = NO; // this affects the complicated color scheme for selection - will not use the "alternateColor" for drawing, but rather the "secondary color" - who knows what it is. This way our texts and icons and progress do NOT disappear on selected rows. [super drawRect:dirtyRect]; } @end And then later, in my NSOutlineView delegate: -(NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item { return [OITRowView new]; } There are, other more complicated @interface OITOutlineCellView : NSTableCellView That I makeViewWithIdentifier:owner: and populate in response to NSOutlineView - (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item { Now I noticed that after adding several items (top level) with some thousands sub-level items to my outline-view, and then REMOVING them and reloading all data - my app's memory is ever increasing When I used the memory graph feature in Xcode debugger - I found a huge number (actually identical to the maximum number of rows) - all of the class MYRowView. Interestingly enough - none of my cell-views remained in memory. Now I verified - my data source reported zero items, the UI looks empty, and all functions as expected - except for this "leak" of MYRowView objects. As you can see in the code (copied exactly from my app) I only create them - and return them to the OS - I never hold any reference to these objects. So.... What Am I doing wrong, and what can I do to alleviate the issue?? The methods [NSOutlineView reloadItem:reloadChildren:] and [NSTableView reloadData] are documented to "release all related views" -- but obviously they DONT. my app gets to ~300 MB (started at 20MB) with about 7500 rows Idea anyone? What to check? what to try?
Posted
by suMac.
Last updated
.
Post marked as solved
4 Replies
707 Views
Assume this over-simplified @protocol I'm using for my XPC-service: @protocol MyMinimalProtocol <NSObject> - (void)getStatusWithReply:(void (^ _Nullable)(NSDictionary * _Nonnull))reply; @end The Client side would then NSXPCConnection *connection = [[NSXPCConnection alloc] initWithMachServiceName:myServiceLabel options:NSXPCConnectionPrivileged]; connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyMinimalProtocol)]; connection.interruptionHandler = ^{ NSLog(@"XPC: connection - interrupted"); }; connection.invalidationHandler = ^{ NSLog(@"XPC: connection - invalidated"); }; [connection resume]; [connection.remoteObjectProxy getStatusWithReply:^(NSDictionary * response) { NSLog(@"XPC service status received - %@", response); }]; So far - so good. My XPC service receives the asynchronous call, schedules it's "status gathering operation" on internal background queue, and returns. Later - when information is available, my XPC service executes the reply-block then, on the remote calling side - I see the log line with the status, as expected. BUT!!! If I add another different code-block argument to the method e.g. @protocol MyMinimalProtocol <NSObject> - (void)getStatusWithReply:(void (^ _Nullable)(NSDictionary * _Nonnull))reply andFailureBlock:(void (^ _Nullable)(NSError * _Nonnull))failureBlock; @end Then all hell breaks loose. Both XPC service and the client crash with hilarious crash reasons I can't decipher. Here's "Client side" caller crash (excerpt - forgive the methods are NOT the simplified ones above) XPC Client(Caller) side crash - excerpt while on the "XPC Service" side, crashes like these: XPC service side crash excerpt I wonder if there's something inherently wrong with having two code-block arguments for an XPC remote method? Another issue. The client XPC calls are asynchronous. They return immediately. The XPC service implementing the remote-call also returns immediately - and it executes the "reply block" far (a minute!) later, on another queue. However, if the XPC service attempts to execute the code-block MORE THAN ONCE, then the client-side code-block is only called ONCE. rest of the executions look benign in the XPC-service side - but never happen on the calling (client) side. Any idea why? can this be overcome? Any thoughts/ideas/references to documentation will be greatly appreciated. I couldn't find any good source on this. Thanks.
Posted
by suMac.
Last updated
.
Post not yet marked as solved
3 Replies
1.8k Views
Hi, I have a long process that I need to report to the User via UNNotificationCenter (local) notifications. Say - scanning a big folder with thousands of files, finding and reporting "bad ones" as they are identified. I currently emit periodic notifications, keeping the same "thread identifier", which causes each new notification to replace the one displayed, thus updating the texts. However - when they become many, the behavior is inconsistent and it looks bad. What I'd like to do is: Have a single DELIVERED notification (displayed on-screen) that will change its contents (say- advances a progress bar, and updates its text periodically. I KNOW that this is possible, because Apple publishes a notification almost identical to what I need in AirDrop UI. When a bunch of documents are dropped by some external device onto my Mac - I receive a notification like this: Which updates until the file transfer is finished. I searched both these forums, the documentation, and other resources, but did NOT find any hint on how to implement such thing. There's something for iOS called "App Extension" that can customize Notification UI - but I did not find any documentation or code-sample or anything - and all I found was descriptions of behavior on iOS, so I'm not sure that's the way to do it on MacOS. Can you please advise?
Posted
by suMac.
Last updated
.
Post not yet marked as solved
0 Replies
581 Views
Hi. My MacOS application (Obj-C, Cocoa MacOS 10.15 or later) sets itself as delegate for UNNotificationCenter, and implements both - (void)userNotificationCenter:(UNUserNotificationCenter *)center        willPresentNotification:(UNNotification *)notification          withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler and - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response          withCompletionHandler:(nonnull void (^)(void))completionHandler It creates and schedules many user-notifications (mostly with no "trigger" meaning - present to the user ASAP and sometimes 2.5 seconds in the future, so to group notifications better. Each notification is also holding som dictionary of "UserInfo" containing some data. My AppDelegates handles user responses (both standard open, cancellation, and "cancel-all") and also custom user-actions I attach to the category. All works well on most Macs and almost always. However - On few Macs, at arbitrary times, users complain that "clicking a notification in Notification Center will not open the app" and that "expected notifications are missing altogether". My logs show the following. Quite frequently I see the following: error log lines: 2023-02-16 17:32:21.413065+0200 0xa58109d Error 0x0 51690 0 <UserNotifications`__104-[UNUserNotificationServiceConnection addNotificationRequest:forBundleIdentifier:withCompletionHandler:]_block_invoke_2.cold.1> myApp: (UserNotifications) [com.apple.UserNotifications:Connections] [com.myCompany.myApp] Adding notification request failed with error: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service on pid 451 named com.apple.usernotifications.usernotificationservice" UserInfo={NSDebugDescription=connection to service on pid 451 named com.apple.usernotifications.usernotificationservice} followed by my own log lines - like this: 2023-02-16 17:32:21.413279+0200 0xa5811b5 Error 0x0 51690 10 <myApp> myApp: [com.myCompany.myApp:UI] NotificationRequest B6096CDE-6229-42AA-A6BC-EBCC06540C53 stage:scanFinished failed to schedule: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service on pid 451 named com.apple.usernotifications.usernotificationservice" UserInfo={NSDebugDescription=connection to service on pid 451 named com.apple.usernotifications.usernotificationservice} Of course - the un-scheduled notification won't appear to the user, but worse - clicks on other, DELIVERED notifications, won't call back my delegate. Strangely enough - few seconds later, sometimes minutes - other attempts to schedule new notifications - succeed without any error. I guess this "connection" is somehow automatically re-established. I looked for information about this error (Domain=NSCocoaErrorDomain Code=4097) and it seems to be pretty generic, and used for many scenarios where connection to some service is lost. Of course my code doesn't maintain any such connection manually/programmatically - I guess it is the [UNUserNotificationCenter currentNotificationCenter] implementation which holds connection to its "daemon" or "agent" or some XPC service. The really bad thing here - is that I DO NOT KNOW how to improve anything here. [UNUserNotificationCenter currentNotificationCenter] Is a singleton, that I can't control, or re-create, to somehow revive the connection There is no way I know of, to tell it to do so, or even to check whether it has a connection. I rely on Notification-Center to maintain my MODEL DATA for scheduled requests, in case my App is relaunched - and now I can't get it. Please advise. this is quite urgent for me. The issue appears more on Big Sur.
Posted
by suMac.
Last updated
.
Post not yet marked as solved
3 Replies
986 Views
Hi. I develop an EndpointSecurity Client (running as root in the system-domain - a launchDaemon defined in /Library/LaunchDaemons/com.company.daemon-name.plist I register to few "AUTH" events, which I handle on a high-priority concurrent dispatch_queue, using fairly efficient code - so to ensure I meet the EndpointSecurity message deadlines. These are the event types I register for:  ES_EVENT_TYPE_AUTH_EXEC,  ES_EVENT_TYPE_AUTH_OPEN, ES_EVENT_TYPE_AUTH_CREATE, ES_EVENT_TYPE_AUTH_CLONE, ES_EVENT_TYPE_AUTH_RENAME, ES_EVENT_TYPE_AUTH_EXCHANGEDATA My daemon seems to handle events in very high loads without a hitch, usually takes round 0.1%-1% of the CPU and almost never more than 10-20MB of RAM - it's very lightweight, and works fine. HOWEVER - on some customer Macs (Enterprise Macs with lots of IT background processes on them - antivirus packages, software-updaters, and remote-control tools, I see crash logs of my tool, occurring usually when the Mac is unattended (late night, or Mac is asleep). They all have this in common. The crash reason: Exception Codes: 0x0000000000000000, 0x0000000000000000 Termination Reason: Namespace ENDPOINTSECURITY, Code 2 EndpointSecurity client terminated because it failed to respond to a message before its deadline I've done fair statistics, and the minimum deadlines I get are ~30 seconds (LOTS OF TIME!!!) whereas my code usually takes no more than 10 milliseconds to respond to EndpointSecurity framework. But the crash log also shows that all "working threads" (code-blocks on my Event-Handling dispatch_queue) are stuck in the same OS call - namely: 6 AppKit 0x7ff80db366b4 -[NSWorkspace isFilePackageAtPath:] + 104 In most cases I have 6 or 8 such concurrent blocks pending. This is not the bottom of the stack - they all look like this: Thread 4:: Dispatch queue: Event Handling Queue 0 libsystem_kernel.dylib 0x7ff809f94e0e __getattrlist + 10 1 CoreServicesInternal 0x7ff80cffbe98 corePropertyProviderPrepareValues(__CFURL const*, __FileCache*, __CFString const* const*, void const**, long, void const*, __CFError**) + 798 2 CoreServicesInternal 0x7ff80cffbb19 prepareValuesForBitmap(__CFURL const*, __FileCache*, _FilePropertyBitmap*, __CFError**) + 394 3 CoreServicesInternal 0x7ff80cff8421 _FSURLCopyResourcePropertyForKeyInternal(__CFURL const*, __CFString const*, void*, void*, __CFError**, unsigned char) + 277 4 CoreFoundation 0x7ff80a084e50 CFURLCopyResourcePropertyForKey + 96 5 CoreFoundation 0x7ff80a09904e -[NSURL getResourceValue:forKey:error:] + 110 6 AppKit 0x7ff80db366b4 -[NSWorkspace isFilePackageAtPath:] + 104 7 ITProtector 0x10ed8da73 0x10ed58000 + 219763 Bottom line is my code - which calls NSWorkspace to determine if a file I need to Authorize is a bundle or not. My conclusion is that the call hangs forever because (Maybe?) LaunchServices, or the File-system service are busy or pushing back or very busy doing something - I don't know, and I can't reproduce on any of my Macs - As I say this randomly happens on customer Macs when they're unattended. I now have two distinct problems using -[NSWorkspace isFilePackageAtPath:] (or any alternative I found so far). The call is usually very fast - but now I can't know in advance how much time it will take. The call and all its alternatives - are synchronous. Haven't found asynchronous replacement I could call, to introduce my own "timeouts" on the issue. I need help here - what can cause this API to hang for over a minute? Where to start looking? last - What activity of the OS can impose such long block on this fairly basic File-system query? Any idea or suggestion or lead would be greatly appreciated. Thanks! Contents of sample ITProtector.ips crash log
Posted
by suMac.
Last updated
.
Post marked as solved
3 Replies
2.3k Views
Two Obj-C processes A and B, communicating via XPC, using NSXPCConnection (the connection is created from an endpoint, unnamed). The method signature is this: - (void)userAction:(NSString *)identifier             update:(OITNFWPreventionStage)stage          eventInfo:(NSDictionary * _Nonnull)actionInfo          withError:(NSError * _Nullable)error              reply:(void (^ _Nullable)(BOOL))reply; I'm using a normal asynchronous proxy id<myProtocol> monitorProxy = [self.monitorConnection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {         NSLog( @"Monitoring XPC proxy call failed: %@", error);     }]; Since the actionInfo I'm using is NSMutableDictionary the gets updated frequently from concurrent queues and thread - I synchronize ALL my calls from process A to process B on an NSOperationQueue     NSOperationQueue *monitorUpdateQueue = [[NSOperationQueue alloc] init];     monitorUpdateQueue.name = @"monitoring queue";     monitorUpdateQueue.maxConcurrentOperationCount = 1;     monitorUpdateQueue.qualityOfService = NSQualityOfServiceUtility; My calls typically look like this:     [monitorUpdateQueue addOperationWithBlock:^{         actionInfo[@"Files"] = [fileEvents valueForKeyPath:@"dictionary"]; // some NSArray of NSDictionaries         actionInfo[@"stage"] = ActionStagePreblocked;         [monitorProxy userAction:userActionIdentifier update:ActionStagePreblocked eventInfo:actionInfo withError:nil reply:^(BOOL reported) {             NSLog(@"Action reported");         }];     }]; Now every now and then, Process A (the caller) crashes inside this remote call... I Full crash log. Couldn't attach .ips file Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x00004c52f8b94400 Exception Codes: 0x0000000000000001, 0x00004c52f8b94400 Exception Note: EXC_CORPSE_NOTIFY Termination Reason: Namespace SIGNAL, Code 11 Segmentation fault: 11 Terminating Process: exc handler [83282] VM Region Info: 0x4c52f8b94400 is not in any region. Bytes after previous region: 83438207583233 Bytes before following region: 21633872346112 REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL commpage (reserved) 1000000000-7000000000 [384.0G] ---/--- SM=NUL ...(unallocated) ---> GAP OF 0x5f9000000000 BYTES MALLOC_NANO 600000000000-600008000000 [128.0M] rw-/rwx SM=PRV and the thread's stack looks like this: Thread 10 Crashed:: Dispatch queue: monitoring queue (QOS: UTILITY) 0 libobjc.A.dylib 0x18a7e8310 objc_retain + 16 1 Foundation 0x18b8eb4a8 -[NSDictionary(NSDictionary) encodeWithCoder:] + 596 2 Foundation 0x18b8ba5f4 -[NSXPCEncoder _encodeObject:] + 520 3 Foundation 0x18b8b9ae4 _NSXPCSerializationAddInvocationArgumentsArray + 276 4 Foundation 0x18b8b95fc -[NSXPCEncoder _encodeInvocation:isReply:into:] + 256 5 Foundation 0x18b8b8798 -[NSXPCConnection _sendInvocation:orArguments:count:methodSignature:selector:withProxy:] + 1356 6 CoreFoundation 0x18aa08040 ___forwarding___ + 1088 7 CoreFoundation 0x18aa07b40 _CF_forwarding_prep_0 + 96 8 myproc 0x10041370c __84-[myproc scanContentOfFilesInEvents:userActionInfo:monitorProxy:monitoringQueue:]_block_invoke_2.1588 + 1064 (myproc.m:3690) 9 Foundation 0x18b8e0600 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 24 10 Foundation 0x18b8e04a8 -[NSBlockOperation main] + 104 11 Foundation 0x18b8e0438 __NSOPERATION_IS_INVOKING_MAIN__ + 24 12 Foundation 0x18b8df67c -[NSOperation start] + 804 13 Foundation 0x18b8df350 __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 24 14 Foundation 0x18b8df204 __NSOQSchedule_f + 184 15 libdispatch.dylib 0x18a7ad990 _dispatch_block_async_invoke2 + 148 16 libdispatch.dylib 0x18a79ebac _dispatch_client_callout + 20 17 libdispatch.dylib 0x18a7a2080 _dispatch_continuation_pop + 504 18 libdispatch.dylib 0x18a7a16dc _dispatch_async_redirect_invoke + 596 19 libdispatch.dylib 0x18a7b031c _dispatch_root_queue_drain + 396 20 libdispatch.dylib 0x18a7b0b58 _dispatch_worker_thread2 + 164 21 libsystem_pthread.dylib 0x18a959574 _pthread_wqthread + 228 22 libsystem_pthread.dylib 0x18a9582c4 start_wqthread + 8 Sorry for the terrible formatting, I could not attach the .ips file, but I attached its full text. My question: When I'm passing an NSMutableDictionary to the remote proxy. Is it received "mutable" on the other side? and while it is being worked in on the receiving side, what happens if I modify it on the calling side (process A) ? How do Mutable objects behave on XPC calls? The Dictionary I'm moving only contains basic "plist approved" entries - NSString, NSNumber, NSDate, and collections (NSArray, NSDictionary). That's all. No custom classes there. I will be most grateful for any idea or hint.
Posted
by suMac.
Last updated
.
Post not yet marked as solved
7 Replies
2.2k Views
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
Posted
by suMac.
Last updated
.
Post marked as solved
3 Replies
1.4k Views
Hi, I’m required to identify file content type (e.g. - tell you that a file is in PDF format, even if the user forced its name to end with .docX, .txt, or even removed it altogether. In other words - identify file type by its real contents. I need to do this fast, for lots of files.  I searched in vain through the MacOS different APIs/Frameworks, from LaunchServices, via MDLS, NSWorkspace, NSURL, and NSFileManager — to no avail. These all provide wonderful APIs for identifying file types - but miserably report the file type as “Microsoft Word” if its filename extension has been set to “.doc” or “.docx”, no matter the content. I then found the ‘file’ command-line in Terminal which does EXACTLY what I want, and reports the correct type every time (well maybe it fails somethings, but I haven’t seen it fail once so far.) Reading ‘man file’ I leaned that it examines a file in 3 stages. stat(2) to start with (identifying Unix things like pipes, sockets, symbolic links etc.) then, it works using some 'unix style' thing called “magic number” mechanism, that employs a “compiled magic file” /usr/share/file/magic.mgc containing “binary signatures” or special “magic numbers” at known offsets that allow quick identification of file formats. Tiny hacking into this file using ’strings’ command I found a rather huge list of formats identifiable by MacOS out of the box - plus - according to man page of file , you should be able to add more “magic” files yourself! However, I wouldn't want to spawn a 'file' command process every time I need to identify a file. I'd rather call some code, or framework from within my process. (This process is of high sensitivity - it is an "Endpoint Security Client" and has lots of restrictions. Is there any public API (Cocoa, Unix, Posix, Core-Foundation, anything!) that will use this "Magic" mechanism to tell me the type of a file? Thank you very much.
Posted
by suMac.
Last updated
.
Post not yet marked as solved
4 Replies
1.7k Views
Hi, I have a LaunchAgent (whose .plist is in /Library/LaunchAgents/ as expected) It is build as a single un-bundled binary, and installed away from regular user access at /Library/MyCompany/executables/ When I try to init a reference to UNUserNotificationCenter: UNUserNotificationCenter *unc = [UNUserNotificationCenter currentNotificationCenter]; I immediately have an exception, like thus: 2021-05-02 19:57:39.297823+0300 agent[12504:225879] *** Assertion failure in +[UNUserNotificationCenter currentNotificationCenter], /AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/UserNotifications/UserNotifications-281.6/UNUserNotificationCenter.m:44 2021-05-02 19:57:39.299464+0300 agent[12504:225879] [General] An uncaught exception was raised 2021-05-02 19:57:39.299518+0300 agent[12504:225879] [General] bundleProxyForCurrentProcess is nil: mainBundle.bundleURL file:///Users/mshneor/Library/Developer/Xcode/DerivedData/agent-dgvdlbtivmsjdgdkhhjwaxgrkyht/Build/Products/Debug/ Obviously the problem is bundleProxyForCurrentProcess is nil: mainBundle.bundleURL Now... I could try to bundle my agent somehow, but that's a bit awkward, and I don't know how - it's not an App, and It's not a part of any App, and I don't know about a special "Code Bundle" type (like plugins etc.) that fits a LaunchAgent. Moreover - it will never be "launched" by it's info.plist in the bundle - only via the .plist in /Library/LaunchAgents by launchd. So... is it possible to work around this? Could I somehow tell my Agent's process "Here is our main-bundle" at runtime, and provide a special bundle the agent is not embedded in? what does UNUserNotificationCenter need from main bundle? maybe I can supply this programmatically in advance? Any hint will be greatly appreciated.
Posted
by suMac.
Last updated
.
Post not yet marked as solved
0 Replies
599 Views
Hi. I'm writing a framework, and invest a lot in commenting classes, methods, etc. My API is Objective-C, but the issue is same with Swift code too. If I follow Apple's documentation about the Markup language of code-comments, then in Xcode QuickHelp lot of my formatting simply does not show. Here are Apple docs on the matter Here is a sample of class definition comment, /*!  @brief Comprehensive set of prevention rules.  @discussion OITPreventionSettings instance encapsulates a complete set of prevention "rules" dictated and provided by Backend, controlling the behavior of ITProtector.  At any moment, there can only be a single "ruling" instance of OITPreventionSettings, assigned (synchronously and atomically) to the OITPrevention, while other instances may exist in other states of initialization/demise.  Responsibilities of the class:  *  Receive and store a "Settings JSON" from backend (either as File, String or NSDictionary)  *  Parse the settings JSON, extract relevant policy clauses, parse their array of prevention rules  *  Provide comprehensive parsing errors if rules cannot be applied.  *  Compile and own a hierarchy of OITPreventionPolicy and OITPreventionRule objects, with internal representation of the rules, that can be used to fast-decide authorization/prevention events.  *  Apply rules to Endpoint-Security event, returning both verdict (actions bit-mask), and the rule-Id responsible for the decision.  Irresponsibilities of the class:  *  to decide *when* to become the "active" settings  *  to apply itself and evaluate ES events  *  Being immutable, It can't change its policies and rules nor their internal representations. Any change of settings require a new instance of OITPreventionSettings.  */ @interface OITPreventionSettings : NSObject { } As you can see in the attached Xcode quick-help window screenshot , none of the lists appear as lists, and even line-breaks don't apply. Many other formatting rules don't show either. Is this Apple bug? my misunderstanding? what's the point in documenting something that doesn't and actually never worked?
Posted
by suMac.
Last updated
.
Post marked as solved
4 Replies
1.3k Views
I apologize for attempting this monstrosity, but... It is forced on me. I have a .jar implementing logic that I receive prebuilt. This .jar exports some API you can use (... in Java...) to request information about file-system items you hand in as paths. This .jar is compatible with Java 11, and runs with the "Zulu" Java VM. I need to "wrap" it in some way that will behave like a global daemon, launched and managed by launchd, available to other components (apps, user-agents and global-daemons) in our product. Running it as a global daemon is a breeze - simply place a .plist in /Library/LaunchDaemons/myMonster.plist and have the java -jar <path to my .jar> arguments, throw in a 'keep alive' option, and it runs. Now... It makes sense for other components to pass it "queries" and receive "results" via XPC connection. First, because this way I could deny connection to unknown components, I could have a secure connection, and also integrate nicely from the other components ObjC code. However... XPC isn't something available in JDK, and the actual executable launched is the Zulu java command binary of course, that I can't modify. So... I tried to use JNA (Java Native Access) and (with much tears and sweat) get my java code to create an xpc connection (as client! not "service") to another XPC Service already running. Also, I was only able to do it via xpc.h (the old C API. not ObjC NSXPCConnection as of yet). The documentation on old C-style XPC Services is very thin, almost nothing... and the only thing about Creating an XPC Service of this style says: // If you are writing a low-level (C-based) XPC service, implement a minimal main function to register your event handler, as shown in the following code listing. Replace my_event_handler with the name of your event handler function. int main(int argc, const char *argv[]) { xpc_main(my_event_handler); // The xpc_main() function never returns. exit(EXIT_FAILURE); } Which of course, I can't do! I don't control the process 'main()' entry point (it's java...) and I can't block anything. So here's my question: Where in the lifecycle of a Java program can I call on my native code to set up The XPC Service? Is there a non-blocking alternative to xpc_main(my_event_handler) I can use to start my service? I know this calls for multi-disciplinary knowledge but any hint or idea will be greatly appreciated. Thanks!
Posted
by suMac.
Last updated
.
Post not yet marked as solved
0 Replies
971 Views
Hi. I'm implementing UNUserNotificationCenter delegate method: userNotificationCenter(_:didReceive:withCompletionHandler:) in MacOS Big-Sur and Catalina, in order to be notified when user dismisses local notifications of my App. (we also handle UNNotificationDefaultActionIdentifier to focus our UI on the item related to the clicked notification, and we also support 2 custom actionIDs. It all basically works, and even the UNNotificationDismissActionIdentifier arrives at the delegate when user dismisses a single notification. (presses the X shaped button on the top-left). However, when we have a thread of notifications (grouped by some unique threadID, and looks like a stack onscreen) and the user presses the X and then the "Clear all" button to dismiss the whole thread -- the delegate is only called once, for one arbitrary notification in the stack, and there is no way for me to know that a whole thread was dismissed. Am I doing something wrong? Is this a limitation of the delegate protocol? Is this a bug in UNUserNotificationCenter implementation? Is there a workaround, or some trick to know that a whole bunch of notifications were dismissed? Here's my delegate code (sans irrelevant code...) // Handle notification back-actions - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response          withCompletionHandler:(nonnull void (^)(void))completionHandler API_AVAILABLE(macos(10.14)) {     NSString *actionId = response.actionIdentifier;     UNNotificationRequest *request = response.notification.request;     NSString *requestId = request.identifier;     NSString *userActionIdentifier = request.content.threadIdentifier;     NSDictionary * myInfo = request.content.userInfo;     if ([actionId isEqualToString:UNNotificationDefaultActionIdentifier]) { // user opened the application from the notification         if ([self.delegate respondsToSelector:@selector(displayThread:forInfo)]) {             dispatch_async(dispatch_get_main_queue(), ^{                 [self.delegate displayThread:userActionIdentifier forInfo: myInfo];             });         }         completionHandler();         return;     }         NSString *userReply = nil;     if ([actionId isEqualToString:UNNotificationDismissActionIdentifier]) { // user dismissed the notification         os_log_debug(myLog, "Notification: %{public}@ dismissed", requestId);     }     else { // custom action         NSString *categoryId = response.notification.request.content.categoryIdentifier;         if ([categoryId isEqualToString:OITNotification.copyAction]) {             os_log_debug(myLog, "Notification: %{public}@ dismissed with custom action:%{public}@", requestId, actionId);         }         if ([categoryId isEqualToString:OITPreventionNotification.copyActionWithReply]) {             if ([actionId isEqualToString:@"other"]) {                 userReply = [(UNTextInputNotificationResponse*)response userText];                 os_log_debug(myLog, "Notification: %{public}@ User custom reply: %{public}@", requestId, userReply);             }             else {                 userReply = actionId;                 os_log_debug(myLog, "Notification: %{public}@ User Action: %{public}@", requestId, actionId);             }         }     }     if ([self.delegate respondsToSelector:@selector(notification:dismissedWithReply:)]) {         [self.delegate notification:myInfo dismissedWithReply:userReply];     }     [self removeModelIdentifiers:@[response.notification.request.identifier]];     completionHandler();     return; } Please ignore the tedious handling of custom actions - my problem is only with "Clear All" thread dismissals. I bring the code just for completeness, in the hopes that someone sees a fault I can't see --- Thank you very much.
Posted
by suMac.
Last updated
.
Post not yet marked as solved
0 Replies
842 Views
I believe this is a general problem when writing low-level security and monitoring tools. The EndpointSecurity framework provides lots of information for the events it allows to Monitor (and/or Authorize) but all these are "kernel level" details, that usually don't allow understanding (and aggregating) low-level events according to User Intent e.g. Saving all attachments of an e-mail message from Apple Mail program to a USB-disk, will create a long series of low-level file-system events, some preliminary (touching and verifying the destination can fit the attachments) some for moving/copying, some for rounding-up, some for indexing those files on the new volume, and so on. Is it possible to somehow obtain the NSUserActivity object/instance/Identifier related to such low-level event? Is the kernel itself aware of user activities? Any hint or suggestion or strategy will be greatly appreciated
Posted
by suMac.
Last updated
.
Post marked as solved
2 Replies
1.8k Views
My new EndpointSecurity client code receives event messages of type esmessaget. These contain 3 time values. struct timespec time; uint64_t mach_time; uint64_t deadline; To interpret the deadline I refer to the mach_time. However, when I wish to log/formet/otherwise-consider the "wall time" where the event was created - I need to translate the timespec into NSDate (hmm... NSTimeInterval?) and I fail to find any documentation or hint about the right way to do so. What I tried so far looks like this: NSDate * timestamp = [NSDate dateWithTimeIntervalSince1970:(double)(message-&gt;time.tv_sec) + (double)(message-&gt;time.tv_sec) / 1E9 ]; which at least mathematically seems reasonable, but I'm not sure this is the right answer, and I don't know anything about the behaviour of timespec, its origin and accuracy, and whether or not it is consistent. Can anyone shed a little light on the relation between these two time formats? Thanks!
Posted
by suMac.
Last updated
.
Post marked as solved
1 Replies
990 Views
My agent/service relies on Accessibility APIs. Being installed by IT on all corporate Macs, it receives its permission to use these APIs via a configuration profile installed on the Mac, and not via the System-Preferences Security &amp; Privacy panel (Privacy tab, Accessibility item). Problem is - if that profile is removed, or changed to remove this permission - The agent currently has no way to know it, and will hang on the next call to some AX API. our code calls  @result Returns TRUE if the current process is a trusted accessibility client, FALSE if it is not.  */ extern Boolean AXIsProcessTrustedWithOptions (CFDictionaryRef __nullable options) CF_AVAILABLE_MAC(10_9); before using other AX APIs, but sadly - the method returns true even when accessibility has been denied by removing the profile. By contrast - if If user manually un-checks the Accessibility for this agent in the System-Preferences panel - the function returns false immediately. If after removing the profile, I kill my agent (launchd then relaunches it) - then calling AXIsProcessTrustedWithOptions returns false as expected. This seems to be a bug of some kind or incomplete behaviour, but I need a workaround as soon as possible. My first "wish" would be to be able to register for and receive some system-wide NSNotification about "configuration profile changes", at which time, I could decide to exit my agent, and relaunch with accessibility permissions synchronised. Or is there any AX internal notification I could register for? haven't found any. Any clue would be greatly appreciated.
Posted
by suMac.
Last updated
.