From d7b4648a90d8fc32a781333abb85f6045d0abeed Mon Sep 17 00:00:00 2001 From: Patrick Wardle Date: Sun, 24 Sep 2017 20:45:02 -1000 Subject: [PATCH] v 1.2.0 high sierra compatibility improved nstask execution alleviated need for 'sudo' refactored code to find camera/mic process pushed various XPC intensive stuff into background --- Installer.xcodeproj/project.pbxproj | 16 +- Installer/AppDelegate.m | 3 + Installer/Configure.m | 64 ++---- Installer/ConfigureWindowController.m | 3 + Installer/Info.plist | 4 +- Installer/main.h | 4 +- Installer/main.m | 153 ++++++------- LoginItem/AVMonitor.m | 238 +++++++++++++++------ LoginItem/AppDelegate.m | 83 ++++++- LoginItem/Info.plist | 4 +- LoginItem/RemeberWindowController.h | 1 - LoginItem/RemeberWindowController.m | 3 - LoginItem/StatusBarMenu.m | 6 + LoginItem/main.m | 44 +++- MainApp/AppDelegate.m | 5 +- MainApp/Info.plist | 4 +- MainApp/Rules.xib | 2 +- OverSight.xcodeproj/project.pbxproj | 14 +- OverSightXPC/Enumerator.h | 8 +- OverSightXPC/Enumerator.m | 297 ++++++++++++++++++-------- OverSightXPC/Info.plist | 4 +- OverSightXPC/OverSightXPC.m | 28 ++- OverSightXPC/main.h | 2 - OverSightXPC/main.m | 23 +- Shared/AboutWindow.xib | 8 +- Shared/Consts.h | 6 +- Shared/Exception.h | 5 +- Shared/Logging.h | 8 +- Shared/Logging.m | 30 ++- Shared/Utilities.h | 16 +- Shared/Utilities.m | 140 ++++++++---- Shared/XPCProtocol.h | 4 + contributors.txt | 1 + 33 files changed, 822 insertions(+), 409 deletions(-) create mode 100644 contributors.txt diff --git a/Installer.xcodeproj/project.pbxproj b/Installer.xcodeproj/project.pbxproj index 3ee8cd2..a7934df 100644 --- a/Installer.xcodeproj/project.pbxproj +++ b/Installer.xcodeproj/project.pbxproj @@ -172,7 +172,7 @@ 7D24C85D1D2CDEA7009932EE /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Objective-See"; TargetAttributes = { 7D24C8641D2CDEA7009932EE = { @@ -229,7 +229,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#for normal builds\n# ->just copy in app\n#cp -R -f $BUILT_PRODUCTS_DIR/OverSight.app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources/\n\n#for achieving\n# ->first build OverSight in release mode (don't archive it)\n# then archive (this) installer app\ncp -R -f ~/objective-see/OverSight/DerivedData/OverSight/Build/Products/Release/OverSight.app ~/objective-see/OverSight/DerivedData/OverSight/Build/Intermediates/ArchiveIntermediates/Installer/BuildProductsPath/Release/OverSight_Installer.app/Contents/Resources/\n"; + shellScript = "#for normal builds\n# ->just copy in app\n#cp -R -f $BUILT_PRODUCTS_DIR/OverSight.app $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources/\n\n#for achieving\n# ->first build OverSight in release mode (don't archive it)\n# then archive (this) installer app\n#cp -R -f ~/objective-see/OverSight/DerivedData/OverSight/Build/Products/Release/OverSight.app ~/objective-see/OverSight/DerivedData/OverSight/Build/Intermediates/ArchiveIntermediates/Installer/BuildProductsPath/Release/OverSight_Installer.app/Contents/Resources/\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -273,14 +273,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -319,14 +325,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/Installer/AppDelegate.m b/Installer/AppDelegate.m index 33c6a3c..5f2951f 100644 --- a/Installer/AppDelegate.m +++ b/Installer/AppDelegate.m @@ -72,6 +72,9 @@ bail: //alloc/init configureWindowController = [[ConfigureWindowController alloc] initWithWindowNibName:@"ConfigureWindowController"]; + //indicated title bar is tranparent (too) + self.configureWindowController.window.titlebarAppearsTransparent = YES; + //display it // ->call this first to so that outlets are connected [self.configureWindowController display]; diff --git a/Installer/Configure.m b/Installer/Configure.m index c4840b5..fd58996 100644 --- a/Installer/Configure.m +++ b/Installer/Configure.m @@ -117,7 +117,6 @@ //no errors wasConfigured = YES; -//bail bail: return wasConfigured; @@ -154,8 +153,8 @@ bail: //path to login item NSString* loginItem = nil; - //logged in user - NSString* user = nil; + //logged in user info + NSMutableDictionary* userInfo = nil; //white list NSString* whiteList = nil; @@ -184,7 +183,7 @@ bail: //remove xattrs // ->otherwise app translocation causes issues - execTask(XATTR, @[@"-cr", appPathDest], YES); + execTask(XATTR, @[@"-cr", appPathDest], NO); //dbg msg #ifdef DEBUG @@ -194,9 +193,9 @@ bail: //init path to login item loginItem = [appPathDest stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"]; - //get user - user = loggedinUser(); - if(nil == user) + //get user info + userInfo = loggedinUser(); + if(nil == userInfo[@"user"]) { //err msg logMsg(LOG_ERR, @"failed to determine logged-in user"); @@ -206,7 +205,7 @@ bail: } //create app support directory - if(YES != [self createAppSupport:user]) + if(YES != [self createAppSupport:userInfo[@"user"]]) { //err msg logMsg(LOG_ERR, @"failed to create app support directory for current user"); @@ -221,7 +220,7 @@ bail: #endif //init path to whitelist - whiteList = [[NSString pathWithComponents:@[@"/Users/", user, APP_SUPPORT_DIRECTORY]] stringByAppendingPathComponent:FILE_WHITELIST]; + whiteList = [[NSString pathWithComponents:@[@"/Users/", userInfo[@"user"], APP_SUPPORT_DIRECTORY]] stringByAppendingPathComponent:FILE_WHITELIST]; //if whitelist exists // ->make sure it's owned by root @@ -232,8 +231,7 @@ bail: } //call into login item to install itself - // ->runs as logged in user, so can access user's login items, etc - execTask(SUDO, @[@"-u", user, loginItem, [NSString stringWithUTF8String:CMD_INSTALL]], YES); + execTask(loginItem, @[[NSString stringWithUTF8String:CMD_INSTALL]], NO); //dbg msg #ifdef DEBUG @@ -266,7 +264,6 @@ bail: //no error wasInstalled = YES; -//bail bail: return wasInstalled; @@ -278,35 +275,20 @@ bail: { //flag BOOL bStarted = NO; - - //logged in user - NSString* user = nil; - + //path to login item NSString* loginItem = nil; - //get user - user = loggedinUser(); - if(nil == user) - { - //err msg - logMsg(LOG_ERR, @"failed to determine logged-in user"); - - //bail - goto bail; - } - //init path loginItem = [[APPS_FOLDER stringByAppendingPathComponent:APP_NAME] stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"]; //start it! // ->don't wait, as it won't exit - execTask(SUDO, @[@"-u", user, loginItem], NO); + execTask(loginItem, nil, NO); //happy bStarted = YES; -//bail bail: return bStarted; @@ -350,8 +332,8 @@ bail: //error NSError* error = nil; - //logged in user - NSString* user = nil; + //logged in user info + NSMutableDictionary* userInfo = nil; //uninstall command // ->changed between v1.0 and 1.1+ @@ -385,8 +367,8 @@ bail: #endif //get user - user = loggedinUser(); - if(nil == user) + userInfo = loggedinUser(); + if(nil == userInfo[@"user"]) { //err msg logMsg(LOG_ERR, @"failed to determine logged-in user"); @@ -401,8 +383,7 @@ bail: #endif //call into login item to uninstall itself - // ->runs as logged in user, so can access user's login items, etc - execTask(SUDO, @[@"-u", user, loginItem, uninstallCmd], YES); + execTask(loginItem, @[uninstallCmd], YES); //dbg msg #ifdef DEBUG @@ -436,13 +417,13 @@ bail: #endif //delete app's app support folder - if(YES == [[NSFileManager defaultManager] fileExistsAtPath:[self appSupportPath:user]]) + if(YES == [[NSFileManager defaultManager] fileExistsAtPath:[self appSupportPath:userInfo[@"user"]]]) { //delete - if(YES != [self removeAppSupport:user]) + if(YES != [self removeAppSupport:userInfo[@"user"]]) { //err msg - logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete app support directory %@", [self appSupportPath:user]]); + logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete app support directory %@", [self appSupportPath:userInfo[@"user"]]]); //set flag bAnyErrors = YES; @@ -455,7 +436,7 @@ bail: else { //dbg msg - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"removed app support directory %@", [self appSupportPath:user]]); + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"removed app support directory %@", [self appSupportPath:userInfo[@"user"]]]); } #endif @@ -469,7 +450,6 @@ bail: wasUninstalled = YES; } -//bail bail: return wasUninstalled; @@ -531,7 +511,6 @@ bail: //happy createdDirectory = YES; -//bail bail: return createdDirectory; @@ -580,12 +559,9 @@ bail: //happy removedDirectory = YES; -//bail bail: return removedDirectory; } - @end - diff --git a/Installer/ConfigureWindowController.m b/Installer/ConfigureWindowController.m index 09bd031..875d943 100644 --- a/Installer/ConfigureWindowController.m +++ b/Installer/ConfigureWindowController.m @@ -81,6 +81,9 @@ // ->center, make front, set bg to white, etc -(void)display { + //indicated title bar is tranparent (too) + self.window.titlebarAppearsTransparent = YES; + //center window [[self window] center]; diff --git a/Installer/Info.plist b/Installer/Info.plist index a9b7be4..d30ce7b 100644 --- a/Installer/Info.plist +++ b/Installer/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1.2 + 1.2.0 CFBundleSignature ???? CFBundleVersion - 1.1.2 + 1.2.0 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright diff --git a/Installer/main.h b/Installer/main.h index 8aa5717..644a098 100644 --- a/Installer/main.h +++ b/Installer/main.h @@ -21,9 +21,9 @@ BOOL spawnAsRoot(const char* path2Self); //install -BOOL cmdlineInstall(); +BOOL cmdlineInstall(void); //uninstall -BOOL cmdlineUninstall(); +BOOL cmdlineUninstall(void); #endif /* main_h */ diff --git a/Installer/main.m b/Installer/main.m index 676e46f..b78f825 100644 --- a/Installer/main.m +++ b/Installer/main.m @@ -17,100 +17,101 @@ int main(int argc, const char * argv[]) //return var int retVar = -1; - @autoreleasepool + //pool + @autoreleasepool { + + //handle '-install' / '-uninstall' + // ->this performs non-UI logic for easier automated deployment + if( (argc >= 2) && + ( (0 == strcmp(argv[1], CMD_INSTALL)) || (0 == strcmp(argv[1], CMD_UNINSTALL)) ) ) { - //handle '-install' / '-uninstall' - // ->this performs non-UI logic for easier automated deployment - if( (argc >= 2) && - ( (0 == strcmp(argv[1], CMD_INSTALL)) || (0 == strcmp(argv[1], CMD_UNINSTALL)) ) ) - { - //first check rooot - if(0 != geteuid()) - { - //err msg - printf("\nERROR: '%s' option, requires root\n\n", argv[1]); - - //bail - goto bail; - } - - //handle install - if(0 == strcmp(argv[1], CMD_INSTALL)) - { - //install - if(YES != cmdlineInstall()) - { - //err msg - printf("\nERROR: install failed\n\n"); - - //bail - goto bail; - } - - //dbg msg - printf("OVERSIGHT: install ok!\n"); - - //happy - retVar = 0; - } - - //handle uninstall - else if(0 == strcmp(argv[1], CMD_UNINSTALL)) - { - //uninstall - if(YES != cmdlineUninstall()) - { - //err msg - printf("\nERROR: install failed\n\n"); - } - - //dbg msg - printf("OVERSIGHT: uninstall ok!\n"); - - //happy - retVar = 0; - } - - //bail - goto bail; - - }//args - - //check for r00t - // ->then spawn self via auth exec + //first check rooot if(0 != geteuid()) { - //dbg msg - #ifdef DEBUG - logMsg(LOG_DEBUG, @"non-root installer instance"); - #endif + //err msg + printf("\nERROR: '%s' option, requires root\n\n", argv[1]); - //spawn as root - if(YES != spawnAsRoot(argv[0])) + //bail + goto bail; + } + + //handle install + if(0 == strcmp(argv[1], CMD_INSTALL)) + { + //install + if(YES != cmdlineInstall()) { //err msg - logMsg(LOG_ERR, @"failed to spawn self as r00t"); + printf("\nERROR: install failed\n\n"); //bail goto bail; } + //dbg msg + printf("OVERSIGHT: install ok!\n"); + //happy retVar = 0; } - //otherwise - // ->just kick off app, as we're root now - else + //handle uninstall + else if(0 == strcmp(argv[1], CMD_UNINSTALL)) { - //dbg msg - #ifdef DEBUG - logMsg(LOG_DEBUG, @"root installer instance"); - #endif + //uninstall + if(YES != cmdlineUninstall()) + { + //err msg + printf("\nERROR: install failed\n\n"); + } - //app away - retVar = NSApplicationMain(argc, (const char **)argv); + //dbg msg + printf("OVERSIGHT: uninstall ok!\n"); + + //happy + retVar = 0; } + + //bail + goto bail; + + }//args + + //check for r00t + // ->then spawn self via auth exec + if(0 != geteuid()) + { + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, @"non-root installer instance"); + #endif + + //spawn as root + if(YES != spawnAsRoot(argv[0])) + { + //err msg + logMsg(LOG_ERR, @"failed to spawn self as r00t"); + + //bail + goto bail; + } + + //happy + retVar = 0; + } + + //otherwise + // ->just kick off app, as we're root now + else + { + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, @"root installer instance"); + #endif + + //app away + retVar = NSApplicationMain(argc, (const char **)argv); + } }//pool diff --git a/LoginItem/AVMonitor.m b/LoginItem/AVMonitor.m index 8d8b3e6..ec7b131 100644 --- a/LoginItem/AVMonitor.m +++ b/LoginItem/AVMonitor.m @@ -239,7 +239,7 @@ bail: { //signal sema dispatch_semaphore_signal(waitSema); - + }]; //wait until XPC is done @@ -414,9 +414,7 @@ bail: { //close connection [xpcConnection invalidate]; - - //nil out - xpcConnection = nil; + } return bRet; @@ -474,7 +472,7 @@ bail: } //helper function -// ->determines if video went active/inactive then invokes notification generator method +// determines if video went active/inactive then invokes notification generator method -(void)handleVideoNotification:(CMIOObjectID)deviceID addresses:(const CMIOObjectPropertyAddress[]) addresses { //event dictionary @@ -517,9 +515,14 @@ bail: [devices addObject:@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)}]; } - //send msg to status menu - // ->update menu to show (all) devices & their status - [((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:devices]; + //update status menu + // run on main thread, since its a UI update + dispatch_async(dispatch_get_main_queue(), ^{ + + //update status menu + [((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:devices]; + + }); //add timestamp event[EVENT_TIMESTAMP] = [NSDate date]; @@ -579,9 +582,6 @@ bail: //close connection [xpcConnection invalidate]; - //nil out - xpcConnection = nil; - //dbg msg #ifdef DEBUG logMsg(LOG_DEBUG, [NSString stringWithFormat:@"video procs from XPC: %@", videoProcesses]); @@ -594,7 +594,14 @@ bail: event[EVENT_PROCESS_ID] = processID; //generate notification - [self generateNotification:event]; + // do on main thread since its a UI event + dispatch_async(dispatch_get_main_queue(), ^{ + + //generate notification + [self generateNotification:event]; + + }); + } //if no consumer process was found @@ -605,7 +612,13 @@ bail: event[EVENT_PROCESS_ID] = @0; //generate notification - [self generateNotification:event]; + // do on main thread since its a UI event + dispatch_async(dispatch_get_main_queue(), ^{ + + //generate notification + [self generateNotification:event]; + + }); } //signal sema @@ -625,11 +638,15 @@ bail: //close connection [xpcConnection invalidate]; - //nil out - xpcConnection = nil; - //generate notification - [self generateNotification:event]; + // do on main thread since its a UI event + dispatch_async(dispatch_get_main_queue(), ^{ + + //generate notification + [self generateNotification:event]; + + }); + } //poll for new video procs @@ -694,8 +711,27 @@ bail: // ->invoked when video changes & just calls helper function CMIOObjectPropertyListenerBlock listenerBlock = ^(UInt32 inNumberAddresses, const CMIOObjectPropertyAddress addresses[]) { - //invoke helper function - [self handleVideoNotification:deviceID addresses:addresses]; + //on main thread? + // handle on background thread + if(YES == [NSThread isMainThread]) + { + //invoke in background + // XPC stuff might be slow! + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^{ + + //handle notification + [self handleVideoNotification:deviceID addresses:addresses]; + + }); + } + //on background thread + // just can invoke as it + else + { + //handle notification + [self handleVideoNotification:deviceID addresses:addresses]; + } }; //register (add) property block listener @@ -764,6 +800,9 @@ bail: //event dictionary NSMutableDictionary* event = nil; + //devices + NSMutableArray* devices = nil; + //xpc connection __block NSXPCConnection* xpcConnection = nil; @@ -772,6 +811,9 @@ bail: //init dictionary event = [NSMutableDictionary dictionary]; + + //init array for devices + devices = [NSMutableArray array]; //sync @synchronized (self) @@ -781,10 +823,29 @@ bail: // ->updates 'audioActive' iVar [self setAudioDevStatus:deviceID]; - //send msg to status menu - // ->update menu to show (all) devices & their status - [((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:@[@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)},@{EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:@(self.videoActive)}]]; - + //add camera + if(nil != self.camera) + { + //add + [devices addObject:@{EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:@(self.videoActive)}]; + } + + //add mic + if(nil != self.mic) + { + //add + [devices addObject:@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)}]; + } + + //update status menu + // run on main thread, since its a UI update + dispatch_async(dispatch_get_main_queue(), ^{ + + //update status menu + [((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:devices]; + + }); + //add timestamp event[EVENT_TIMESTAMP] = [NSDate date]; @@ -804,28 +865,28 @@ bail: //set remote object interface xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)]; - + //resume [xpcConnection resume]; - + //init wait semaphore waitSema = dispatch_semaphore_create(0); //tell XPC about audio status // ->for example, when audio is active, will stop baselining [[xpcConnection remoteObjectProxy] updateAudioStatus:self.audioActive reply:^{ - + //signal sema dispatch_semaphore_signal(waitSema); }]; - + //wait until XPC is done // ->XPC reply block will signal semaphore dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER); - - //if video just started - // ->ask for video procs from XPC + + //if audio just started + // ->ask for audio procs from XPC if(YES == self.audioActive) { //dbg msg @@ -843,9 +904,6 @@ bail: //close connection [xpcConnection invalidate]; - //nil out - xpcConnection = nil; - //dbg msg #ifdef DEBUG logMsg(LOG_DEBUG, [NSString stringWithFormat:@"audio procs from XPC: %@", audioProcesses]); @@ -858,7 +916,13 @@ bail: event[EVENT_PROCESS_ID] = processID; //generate notification - [self generateNotification:event]; + // do on main thread since its a UI event + dispatch_async(dispatch_get_main_queue(), ^{ + + //generate notification + [self generateNotification:event]; + + }); } //if no consumer process was found @@ -869,7 +933,13 @@ bail: event[EVENT_PROCESS_ID] = @0; //generate notification - [self generateNotification:event]; + // do on main thread since its a UI event + dispatch_async(dispatch_get_main_queue(), ^{ + + //generate notification + [self generateNotification:event]; + + }); } //signal sema @@ -889,11 +959,14 @@ bail: //close connection [xpcConnection invalidate]; - //nil out - xpcConnection = nil; - //generate notification - [self generateNotification:event]; + // do on main thread since its a UI event + dispatch_async(dispatch_get_main_queue(), ^{ + + //generate notification + [self generateNotification:event]; + + }); } }//sync @@ -930,7 +1003,27 @@ bail: // ->invoked when audio changes & just calls helper function AudioObjectPropertyListenerBlock listenerBlock = ^(UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses) { - [self handleAudioNotification:deviceID]; + //on main thread? + // handle on background thread + if(YES == [NSThread isMainThread]) + { + //invoke in background + // XPC stuff might be slow! + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^{ + + //handle notification + [self handleAudioNotification:deviceID]; + + }); + } + //on background thread + // just can invoke as it + else + { + //handle notification + [self handleAudioNotification:deviceID]; + } }; //add property listener for audio changes @@ -957,6 +1050,9 @@ bail: // ->handles extra logic like ignore whitelisted apps, disable alerts (if user has turned that off), etc -(void)generateNotification:(NSMutableDictionary*)event { + //pool + @autoreleasepool { + //notification NSUserNotification* notification = nil; @@ -1291,6 +1387,8 @@ bail: } } + }//pool + //bail bail: @@ -1307,6 +1405,9 @@ bail: // ->handle rule creation, blocking/killing proc, etc -(void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification { + //pool + @autoreleasepool { + //xpc connection __block NSXPCConnection* xpcConnection = nil; @@ -1524,11 +1625,11 @@ bail: //close connection [xpcConnection invalidate]; - //nil out - xpcConnection = nil; }]; }//user clicked 'block' + + }//pool //bail bail: @@ -1607,9 +1708,13 @@ bail: } //monitor for new procs (video only at the moment) -// ->runs until video is no longer in use (set elsewhere) +// runs until video is no longer in use (set elsewhere) -(void)monitor4Procs { + + //pool + @autoreleasepool { + //xpc connection NSXPCConnection* xpcConnection = nil; @@ -1621,22 +1726,12 @@ bail: logMsg(LOG_DEBUG, @"[MONITOR THREAD] video is active, so polling for new procs"); #endif - //alloc XPC connection - xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"]; - - //set remote object interface - xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)]; - - //set classes - // ->arrays/numbers ok to vend - [xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSNumber class], nil] - forSelector: @selector(getVideoProcs:reply:) argumentIndex: 0 ofReply: YES]; - //resume - [xpcConnection resume]; - //poll while video is active while(YES == self.videoActive) { + //pool + @autoreleasepool { + //init wait semaphore waitSema = dispatch_semaphore_create(0); @@ -1644,6 +1739,20 @@ bail: #ifdef DEBUG logMsg(LOG_DEBUG, @"[MONITOR THREAD] (re)Asking XPC for (new) video procs"); #endif + + //alloc XPC connection + xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"]; + + //set remote object interface + xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)]; + + //set classes + // ->arrays/numbers ok to vend + [xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSNumber class], nil] + forSelector: @selector(getVideoProcs:reply:) argumentIndex: 0 ofReply: YES]; + //resume + [xpcConnection resume]; + //invoke XPC service to get (new) video procs // ->will generate user notifications for any new processes @@ -1652,7 +1761,7 @@ bail: //dbg msg #ifdef DEBUG logMsg(LOG_DEBUG, [NSString stringWithFormat:@"[MONITOR THREAD] found %lu new video procs: %@", (unsigned long)videoProcesses.count, videoProcesses]); - #endif + #endif //generate a notification for each process // ->double check video is still active though... @@ -1672,26 +1781,27 @@ bail: //signal sema dispatch_semaphore_signal(waitSema); + //invalidate + [xpcConnection invalidate]; + }]; //wait until XPC is done // ->XPC reply block will signal semaphore dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER); + + }//pool //nap [NSThread sleepForTimeInterval:5.0f]; }//run until video (camera) is off -//bail + //pool + } + bail: - //close connection - [xpcConnection invalidate]; - - //nil out - xpcConnection = nil; - //dbg msg #ifdef DEBUG logMsg(LOG_DEBUG, @"[MONITOR THREAD] exiting polling/monitor thread since camera is off"); diff --git a/LoginItem/AppDelegate.m b/LoginItem/AppDelegate.m index 0657f47..0399523 100644 --- a/LoginItem/AppDelegate.m +++ b/LoginItem/AppDelegate.m @@ -10,6 +10,7 @@ #import "Logging.h" #import "Utilities.h" #import "AppDelegate.h" +#import "XPCProtocol.h" @interface AppDelegate () @@ -29,16 +30,30 @@ //preferences NSDictionary* preferences = nil; + //logged in user info + NSMutableDictionary* userInfo = nil; + //dbg msg #ifdef DEBUG logMsg(LOG_DEBUG, @"starting login item app logic"); #endif + //get user + userInfo = loggedinUser(); + if(nil == userInfo[@"user"]) + { + //err msg + logMsg(LOG_ERR, @"failed to determine logged-in user"); + + //bail + goto bail; + } + //drop group privs - setgid(getgid()); + setgid([userInfo[@"gid"] intValue]); //drop user privs - setuid(getuid()); + setuid([userInfo[@"uid"] intValue]); //load preferences preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]]; @@ -99,10 +114,12 @@ //dbg msg // ->and to file - logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging intialized"); - + logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging intialized (login item)"); } - + + //spawn 'heartbeat' thread to XPC to keep it open + [NSThread detachNewThreadSelector:@selector(heartBeat) toTarget:self withObject:nil]; + //create/init av event monitor avMonitor = [[AVMonitor alloc] init]; @@ -194,6 +211,60 @@ bail: return; } +//ping XPC service to keep it alive +-(void)heartBeat +{ + //pool + @autoreleasepool { + + //xpc connection + __block NSXPCConnection* xpcConnection = nil; + + //wait semaphore + dispatch_semaphore_t waitSema = nil; + + //alloc XPC connection + xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"]; + + //set remote object interface + xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)]; + + //resume + [xpcConnection resume]; + + //forever + while(YES) + { + //init wait semaphore + waitSema = dispatch_semaphore_create(0); + + #ifdef DEBUG + //dbg msg + logMsg(LOG_DEBUG, @"sending XPC heart beat request"); + #endif + + //XPC service to begin baselining mach messages + // ->wait, since want this to compelete before doing other things! + [[xpcConnection remoteObjectProxy] heartBeat:^(BOOL reply) + { + //signal sema + dispatch_semaphore_signal(waitSema); + + }]; + + //wait until XPC is done + // ->XPC reply block will signal semaphore + dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER); + + //nap + [NSThread sleepForTimeInterval:3.0f]; + } + + }//pool + + return; +} + //going bye-bye // ->close logging -(void)applicationWillTerminate:(NSNotification *)notification @@ -202,7 +273,7 @@ bail: logMsg(LOG_DEBUG|LOG_TO_FILE, @"OverSight ending"); //log msg - logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging deinitialized"); + logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging deinitialized (login item)"); //stop logz deinitLogging(); diff --git a/LoginItem/Info.plist b/LoginItem/Info.plist index a11238c..06aa6dc 100644 --- a/LoginItem/Info.plist +++ b/LoginItem/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1.2 + 1.2.0 CFBundleSignature ???? CFBundleVersion - 1.1.2 + 1.2.0 LSUIElement LSMinimumSystemVersion diff --git a/LoginItem/RemeberWindowController.h b/LoginItem/RemeberWindowController.h index caf1f51..64b16bb 100644 --- a/LoginItem/RemeberWindowController.h +++ b/LoginItem/RemeberWindowController.h @@ -31,7 +31,6 @@ //version label/string @property (weak) IBOutlet NSTextField *windowText; - /* METHODS */ //save stuff into iVars diff --git a/LoginItem/RemeberWindowController.m b/LoginItem/RemeberWindowController.m index b854cad..10c1f02 100644 --- a/LoginItem/RemeberWindowController.m +++ b/LoginItem/RemeberWindowController.m @@ -137,9 +137,6 @@ //close connection [xpcConnection invalidate]; - //nil out - xpcConnection = nil; - }]; } diff --git a/LoginItem/StatusBarMenu.m b/LoginItem/StatusBarMenu.m index 7c38979..6fae470 100644 --- a/LoginItem/StatusBarMenu.m +++ b/LoginItem/StatusBarMenu.m @@ -66,6 +66,10 @@ //create/update status item menu -(void)updateStatusItemMenu:(NSArray*)devices { + //pool + @autoreleasepool + { + //menu NSMenu* menu = nil; @@ -194,6 +198,8 @@ //tie menu to status item self.statusItem.menu = menu; + }//pool + return; } diff --git a/LoginItem/main.m b/LoginItem/main.m index f96ab6c..e3f1c55 100644 --- a/LoginItem/main.m +++ b/LoginItem/main.m @@ -18,14 +18,44 @@ int main(int argc, const char * argv[]) //return var int iReturn = 0; + //logged in user info + NSMutableDictionary* userInfo = nil; + + //pool + @autoreleasepool + { + //dbg msg #ifdef DEBUG - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"starting login item (args: %@/user: %@)", [[NSProcessInfo processInfo] arguments], NSUserName()]); + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"starting login item (args: %@/user: %@/%@)", [[NSProcessInfo processInfo] arguments], NSUserName(), loggedinUser()]); #endif //check for uninstall/install flags, and process to remove from whitelist if(2 == argc) { + //drops privs when installing/uninstalling + // do here, only for these as they then bail + if( (0 == strcmp(argv[1], CMD_INSTALL)) || + (0 == strcmp(argv[1], CMD_UNINSTALL)) ) + { + //get user + userInfo = loggedinUser(); + if(nil == userInfo[@"user"]) + { + //err msg + logMsg(LOG_ERR, @"failed to determine logged-in user"); + + //bail + goto bail; + } + + //drop group privs + setgid([userInfo[@"gid"] intValue]); + + //drop user privs + setuid([userInfo[@"uid"] intValue]); + } + //install if(0 == strcmp(argv[1], CMD_INSTALL)) { @@ -34,9 +64,6 @@ int main(int argc, const char * argv[]) logMsg(LOG_DEBUG, @"running install logic"); #endif - //drop user privs - setuid(getuid()); - //install if(YES != toggleLoginItem([NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]], ACTION_INSTALL_FLAG)) { @@ -74,9 +101,6 @@ int main(int argc, const char * argv[]) logMsg(LOG_DEBUG, @"running uninstall logic"); #endif - //drop user privs - setuid(getuid()); - //uninstall if(YES != toggleLoginItem([NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]], ACTION_UNINSTALL_FLAG)) { @@ -130,7 +154,8 @@ int main(int argc, const char * argv[]) //launch app normally iReturn = NSApplicationMain(argc, argv); -//bail + }//pool + bail: return iReturn; @@ -173,9 +198,6 @@ void unWhiteList(NSString* process, NSNumber* device) //close connection [xpcConnection invalidate]; - - //nil out - xpcConnection = nil; }]; diff --git a/MainApp/AppDelegate.m b/MainApp/AppDelegate.m index 0bc5190..ee18f55 100644 --- a/MainApp/AppDelegate.m +++ b/MainApp/AppDelegate.m @@ -244,7 +244,7 @@ bail: else { //log msg - logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging initialized"); + logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging initialized (main app)"); } } //when logging is disabled @@ -252,7 +252,7 @@ bail: else { //log msg - logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging deinitialized"); + logMsg(LOG_DEBUG|LOG_TO_FILE, @"logging deinitialized (main app)"); //close deinitLogging(); @@ -539,7 +539,6 @@ bail: goto bail; } - //bail bail: diff --git a/MainApp/Info.plist b/MainApp/Info.plist index 26919b0..48b469c 100644 --- a/MainApp/Info.plist +++ b/MainApp/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1.2 + 1.2.0 CFBundleSignature ???? CFBundleVersion - 1.1.2 + 1.2.0 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement diff --git a/MainApp/Rules.xib b/MainApp/Rules.xib index c5204fc..03e7488 100644 --- a/MainApp/Rules.xib +++ b/MainApp/Rules.xib @@ -21,7 +21,7 @@ - + diff --git a/OverSight.xcodeproj/project.pbxproj b/OverSight.xcodeproj/project.pbxproj index 16f08ba..af90098 100644 --- a/OverSight.xcodeproj/project.pbxproj +++ b/OverSight.xcodeproj/project.pbxproj @@ -413,7 +413,7 @@ 8B57559319DA3E9500799E6B /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0820; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Cory Bohon"; TargetAttributes = { 7DC9C8111D641A350017D143 = { @@ -620,14 +620,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -667,14 +673,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; diff --git a/OverSightXPC/Enumerator.h b/OverSightXPC/Enumerator.h index 88df0f1..b255d59 100644 --- a/OverSightXPC/Enumerator.h +++ b/OverSightXPC/Enumerator.h @@ -33,6 +33,12 @@ /* PROPERTIES */ +//camera assistant pid +@property pid_t cameraAssistantProcess; + +//core audio pid +@property pid_t coreAudioProcess; + //flag indicating video is active @property BOOL videoActive; @@ -49,8 +55,6 @@ // ->IOService:/AppleACPIPlatformExpert/IOPMrootDomain/RootDomainUserClient @property(nonatomic, retain)NSMutableDictionary* userClients; - - /* METHODS */ //singleton interface diff --git a/OverSightXPC/Enumerator.m b/OverSightXPC/Enumerator.m index 36087ee..185e81b 100644 --- a/OverSightXPC/Enumerator.m +++ b/OverSightXPC/Enumerator.m @@ -23,8 +23,11 @@ static NSArray* ignoredProcs = nil; @synthesize audioActive; @synthesize userClients; @synthesize videoActive; +@synthesize coreAudioProcess; @synthesize machSendersAudio; @synthesize machSendersVideo; +@synthesize cameraAssistantProcess; + //init -(instancetype)init @@ -78,13 +81,28 @@ static NSArray* ignoredProcs = nil; // ->logic only exec'd while camera/mic is not in use, so these are all just baselined procs -(void)start { - //camera assistant - pid_t cameraAssistant = 0; + //flag + BOOL nap = NO; //baseline forever // ->though logic will skip if video or mic is active (respectively) while(YES) { + //le sleep? + if(nap == YES) + { + //nap + [NSThread sleepForTimeInterval:30]; + } + + //set flag + // from now on, want to wait a bit + nap = YES; + + //pool + @autoreleasepool + { + //sync baselining @synchronized(self) { @@ -97,31 +115,36 @@ static NSArray* ignoredProcs = nil; #endif //find camera assistant - // ->first look for 'VDCAssistant' - cameraAssistant = findProcess(VDC_ASSISTANT); - if(0 == cameraAssistant) + // only do this once, or again, if it died + if( (0 == self.cameraAssistantProcess) || + (YES != isProcessAlive(self.cameraAssistantProcess)) ) { - //look for 'AppleCameraAssistant' - cameraAssistant = findProcess(APPLE_CAMERA_ASSISTANT); + //find camera assistant + // ->first look for 'VDCAssistant' + self.cameraAssistantProcess = findProcess(VDC_ASSISTANT); + if(0 == self.cameraAssistantProcess) + { + //look for 'AppleCameraAssistant' + self.cameraAssistantProcess = findProcess(APPLE_CAMERA_ASSISTANT); + } } - //sanity check - if(0 == cameraAssistant) + //baseline + if(0 != self.cameraAssistantProcess) { - //nap for a minute - [NSThread sleepForTimeInterval:60]; + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"camera assistent process: %d", self.coreAudioProcess]); + #endif - //next - continue; + //enumerate procs that have send mach messages + self.machSendersVideo = [self enumMachSenders:self.cameraAssistantProcess]; + + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined mach senders: %@", (unsigned long)self.machSendersVideo.count, self.machSendersVideo]); + #endif } - - //enumerate procs that have send mach messages - self.machSendersVideo = [self enumMachSenders:cameraAssistant]; - - //dbg msg - #ifdef DEBUG - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined mach senders: %@", (unsigned long)self.machSendersVideo.count, self.machSendersVideo]); - #endif } //only baseline if audio isn't active @@ -132,29 +155,47 @@ static NSArray* ignoredProcs = nil; logMsg(LOG_DEBUG, @"baselining mach senders for audio..."); #endif - //enumerate procs that have send mach messages - self.machSendersAudio = [self enumMachSenders:findProcess(CORE_AUDIO)]; + //find core audio + // only do this once, or again, if it died + if( (0 == self.coreAudioProcess) || + (YES != isProcessAlive(self.coreAudioProcess)) ) + { + //find core audio + self.coreAudioProcess = findProcess(CORE_AUDIO); + } - //dbg msg - #ifdef DEBUG - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined mach senders: %@", (unsigned long)self.machSendersAudio.count, self.machSendersVideo]); - - //dbg msg - logMsg(LOG_DEBUG, @"baselining i/o registry entries for audio..."); - #endif - - //enumerate procs that have i/o registry entries - self.userClients = [self enumDomainUserClients]; - - //dbg msg - #ifdef DEBUG - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined i/or registry senders: %@", (unsigned long)self.userClients.count, self.userClients]); - #endif + //baseline + if(0 != self.coreAudioProcess) + { + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"camera core audio process: %d", self.coreAudioProcess]); + #endif + + //enumerate procs that have send mach messages + self.machSendersAudio = [self enumMachSenders:self.coreAudioProcess]; + + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined mach senders: %@", (unsigned long)self.machSendersAudio.count, self.machSendersVideo]); + + //dbg msg + logMsg(LOG_DEBUG, @"baselining i/o registry entries for audio..."); + #endif + + //enumerate procs that have i/o registry entries + self.userClients = [self enumDomainUserClients]; + + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined i/or registry senders: %@", (unsigned long)self.userClients.count, self.userClients]); + #endif + } } - } + + }//sync - //nap for a minute - [NSThread sleepForTimeInterval:60]; + }//pool } return; @@ -166,18 +207,19 @@ static NSArray* ignoredProcs = nil; //current procs NSMutableArray* videoProcs = nil; + //pool + @autoreleasepool + { + //mach senders NSMutableDictionary* currentSenders = nil; //candidate video procs // ->those that have new mach message NSMutableArray* candidateVideoProcs = nil; - - //pid of camera assistant process - pid_t cameraAssistant = 0; - - //'frontmost' application - pid_t activeApp = -1; + + //foreground app + pid_t activeApp = 0; //alloc candidateVideoProcs = [NSMutableArray array]; @@ -186,17 +228,24 @@ static NSArray* ignoredProcs = nil; // ->prevent baselining thread from doing anything @synchronized(self) { - - //first look for 'VDCAssistant' - cameraAssistant = findProcess(VDC_ASSISTANT); - if(0 == cameraAssistant) + + //find camera assistant + // only do this once, or again, if it died + if( (0 == self.cameraAssistantProcess) || + (YES != isProcessAlive(self.cameraAssistantProcess)) ) { - //look for 'AppleCameraAssistant' - cameraAssistant = findProcess(APPLE_CAMERA_ASSISTANT); + //find camera assistant + // ->first look for 'VDCAssistant' + self.cameraAssistantProcess = findProcess(VDC_ASSISTANT); + if(0 == self.cameraAssistantProcess) + { + //look for 'AppleCameraAssistant' + self.cameraAssistantProcess = findProcess(APPLE_CAMERA_ASSISTANT); + } } //sanity check - if(0 == cameraAssistant) + if(0 == self.cameraAssistantProcess) { //err msg logMsg(LOG_ERR, @"failed to find VDCAssistant/AppleCameraAssistant process"); @@ -207,7 +256,7 @@ static NSArray* ignoredProcs = nil; //get procs that currrently have sent Mach msg to *Assistant // ->returns dictionary of process id, and number of mach messages - currentSenders = [self enumMachSenders:cameraAssistant]; + currentSenders = [self enumMachSenders:self.cameraAssistantProcess]; //dbg msg #ifdef DEBUG @@ -219,7 +268,7 @@ static NSArray* ignoredProcs = nil; { //add any candidate procs // ->those that have new mach message - if( [currentSenders[processID] intValue] > [self.machSendersVideo[processID] intValue]) + if([currentSenders[processID] intValue] > [self.machSendersVideo[processID] intValue]) { //ignore client/requestor if(clientPID == processID.intValue) @@ -270,8 +319,9 @@ static NSArray* ignoredProcs = nil; videoProcs = [self sampleCandidates:candidateVideoProcs]; }//sync + + }//pool -//bail bail: return videoProcs; @@ -283,6 +333,10 @@ bail: //current procs NSMutableArray* audioProcs = nil; + //pool + @autoreleasepool + { + //current mach senders NSMutableDictionary* currentSenders = nil; @@ -302,9 +356,6 @@ bail: //itersection set NSMutableSet* intersection = nil; - //pid of coreaudio process - pid_t coreAudio = 0; - //'frontmost' application pid_t activeApp = -1; @@ -314,16 +365,22 @@ bail: //alloc array newUserClients = [NSMutableArray array]; - //alloc array - candidateAudioProcs = [NSMutableArray array]; - //sync this logic // ->prevent baselining thread from doing anything @synchronized(self) { //find coreaudio - coreAudio = findProcess(CORE_AUDIO); - if(0 == coreAudio) + //find core audio + // only do this once, or again, if it died + if( (0 == self.coreAudioProcess) || + (YES != isProcessAlive(self.coreAudioProcess)) ) + { + //find core audio + self.coreAudioProcess = findProcess(CORE_AUDIO); + } + + //sanity check + if(0 == self.coreAudioProcess) { //err msg logMsg(LOG_ERR, @"failed to find coreaudio process"); @@ -334,7 +391,7 @@ bail: //get procs that currrently have sent Mach msg to core audio // ->returns dictionary of process id, and number of mach messages - currentSenders = [self enumMachSenders:coreAudio]; + currentSenders = [self enumMachSenders:self.coreAudioProcess]; //dbg msg #ifdef DEBUG @@ -509,8 +566,9 @@ bail: audioProcs = [self sampleCandidates:candidateAudioProcs]; }//sync + + }//pool -//bail bail: return audioProcs; @@ -523,6 +581,10 @@ bail: //senders NSMutableDictionary* senders = nil; + //pool + @autoreleasepool + { + //results from 'lsmp' cmd NSString* results = nil; @@ -610,8 +672,9 @@ bail: //add/inc to dictionary senders[processID] = @([senders[processID] unsignedIntegerValue] + 1); } + + }//pool -//bail bail: return senders; @@ -621,6 +684,13 @@ bail: // ->returns dictionary of process id, and number of user client entries -(NSMutableDictionary*)enumDomainUserClients { + //array of RootDomainUserClients + NSMutableDictionary* clients = nil; + + //pool + @autoreleasepool + { + //matching service io_service_t matchingService = 0; @@ -630,9 +700,6 @@ bail: //kids io_registry_entry_t child = 0; - //array of RootDomainUserClients - NSMutableDictionary* clients = nil; - //client creator CFTypeRef creator = 0; @@ -670,6 +737,9 @@ bail: //always release child IOObjectRelease(child); + //unset + child = 0; + //if couldn't get a creator // ->might just not be of RootDomainUserClient, so skip if(0 == creator) @@ -696,9 +766,11 @@ bail: //release CFRelease(creator); + + //unset + creator = 0; } -//bail bail: //release iterator @@ -706,6 +778,9 @@ bail: { //release IOObjectRelease(iterator); + + //unset + iterator = 0; } //release obj @@ -713,10 +788,14 @@ bail: { //release IOObjectRelease(matchingService); + + //unset + matchingService = 0; } + + }//pool return clients; - } //invoke 'sample' to confirm candidates are using CMIO/video/av inputs @@ -726,6 +805,10 @@ bail: //av procs NSMutableArray* avProcs = nil; + //pool + @autoreleasepool + { + //results from 'sample' cmd NSString* results = nil; @@ -807,6 +890,8 @@ bail: [avProcs addObject:processID]; } + }//pool + return avProcs; } @@ -814,6 +899,10 @@ bail: // ->this looks for that file and deletes it -(void)deleteSampleFile:(NSString*)processPath { + //pool + @autoreleasepool + { + //error NSError* error = nil; @@ -864,6 +953,8 @@ bail: } }//all files + + }//pool //bail bail: @@ -876,9 +967,10 @@ bail: // ->extra logic is executed to 'refresh' iVars when video is disabled -(void)updateVideoStatus:(BOOL)isEnabled { - //camera assistant - pid_t cameraAssistant = 0; - + //pool + @autoreleasepool + { + //sync @synchronized(self) { @@ -889,16 +981,23 @@ bail: // ->re-enumerate mach senders if(YES != isEnabled) { - //first, look for 'VDCAssistant' - cameraAssistant = findProcess(VDC_ASSISTANT); - if(0 == cameraAssistant) + //find camera assistant + // only do this once, or again, if it died + if( (0 == self.cameraAssistantProcess) || + (YES != isProcessAlive(self.cameraAssistantProcess)) ) { - //look for 'AppleCameraAssistant' - cameraAssistant = findProcess(APPLE_CAMERA_ASSISTANT); + //find camera assistant + // ->first look for 'VDCAssistant' + self.cameraAssistantProcess = findProcess(VDC_ASSISTANT); + if(0 == self.cameraAssistantProcess) + { + //look for 'AppleCameraAssistant' + self.cameraAssistantProcess = findProcess(APPLE_CAMERA_ASSISTANT); + } } //sanity check - if(0 == cameraAssistant) + if(0 == self.cameraAssistantProcess) { //err msg logMsg(LOG_ERR, @"failed to find VDCAssistant/AppleCameraAssistant process"); @@ -908,11 +1007,13 @@ bail: } //enumerate mach senders - self.machSendersVideo = [self enumMachSenders:cameraAssistant]; + self.machSendersVideo = [self enumMachSenders:self.cameraAssistantProcess]; } }//sync + }//pool + //bail bail: @@ -923,6 +1024,10 @@ bail: // ->extra logic is executed to 'refresh' iVars when audio is disabled -(void)updateAudioStatus:(BOOL)isEnabled { + //pool + @autoreleasepool + { + //sync @synchronized(self) { @@ -933,14 +1038,30 @@ bail: // ->re-enumerate mach senders & i/o registry user clients if(YES != isEnabled) { - //enumerate mach senders - self.machSendersAudio = [self enumMachSenders:findProcess(CORE_AUDIO)]; + //find coreaudio + //find core audio + // only do this once, or again, if it died + if( (0 == self.coreAudioProcess) || + (YES != isProcessAlive(self.coreAudioProcess)) ) + { + //find core audio + self.coreAudioProcess = findProcess(CORE_AUDIO); + } - //enumerate i/o registry user clients - self.userClients = [self enumDomainUserClients]; + //enumerate + if(0 != self.coreAudioProcess) + { + //enumerate mach senders + self.machSendersAudio = [self enumMachSenders:self.coreAudioProcess]; + + //enumerate i/o registry user clients + self.userClients = [self enumDomainUserClients]; + } } } - + + }//pool + return; } diff --git a/OverSightXPC/Info.plist b/OverSightXPC/Info.plist index ba51a3b..2330d25 100644 --- a/OverSightXPC/Info.plist +++ b/OverSightXPC/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 1.1.2 + 1.2.0 CFBundleSignature ???? CFBundleVersion - 1.1.2 + 1.2.0 NSHumanReadableCopyright Copyright (c) 2017 Objective-See. All rights reserved. XPCService diff --git a/OverSightXPC/OverSightXPC.m b/OverSightXPC/OverSightXPC.m index 4da7fa8..0724746 100644 --- a/OverSightXPC/OverSightXPC.m +++ b/OverSightXPC/OverSightXPC.m @@ -12,7 +12,6 @@ #import "Enumerator.h" #import "OverSightXPC.h" - @implementation OverSightXPC @synthesize machSenders; @@ -23,7 +22,7 @@ -(void)initialize:(void (^)(void))reply { //start enumerating - // ->will forever baseline current mach msg procs + // will forever baseline current mach msg procs [NSThread detachNewThreadSelector:@selector(start) toTarget:[Enumerator sharedManager] withObject:nil]; //reply @@ -32,6 +31,23 @@ return; } +//heartbeat +// need as otherwise kernel might kill XPC +-(void)heartBeat:(void (^)(BOOL))reply +{ + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, @"heartbeat request"); + #endif + + //nap + [NSThread sleepForTimeInterval:3.0f]; + + reply(YES); + + return; +} + //call into emumerate to get (new) video proc -(void)getVideoProcs:(BOOL)polling reply:(void (^)(NSMutableArray *))reply { @@ -69,7 +85,7 @@ { //set status [[Enumerator sharedManager] updateAudioStatus:status]; - + //reply reply(); @@ -118,7 +134,6 @@ //happy wasAdded = YES; -//bail bail: //reply @@ -192,7 +207,6 @@ bail: } } -//bail bail: //reply @@ -220,7 +234,6 @@ bail: //happy wasKilled = YES; -//bail bail: //reply @@ -236,7 +249,4 @@ bail: exit(0); } - @end - - diff --git a/OverSightXPC/main.h b/OverSightXPC/main.h index 7a43b43..513fba0 100644 --- a/OverSightXPC/main.h +++ b/OverSightXPC/main.h @@ -51,6 +51,4 @@ OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement) @end - - #endif /* main_h */ diff --git a/OverSightXPC/main.m b/OverSightXPC/main.m index fc22736..485c5a0 100644 --- a/OverSightXPC/main.m +++ b/OverSightXPC/main.m @@ -7,7 +7,7 @@ // #import "main.h" - +#import "Logging.h" /* GLOBALS */ @@ -26,8 +26,8 @@ pid_t clientPID = 0; @implementation ServiceDelegate //automatically invoked -//->allows NSXPCListener to configure/accept/resume a new incoming NSXPCConnection -// note: we only allow binaries signed by Objective-See to talk to this! +// allows NSXPCListener to configure/accept/resume a new incoming NSXPCConnection +// note: we only allow binaries signed by Objective-See to connect & talk to this! -(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { //flag @@ -39,6 +39,11 @@ pid_t clientPID = 0; //signing req string NSString *requirementString = nil; + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, @"new client connection"); + #endif + //init signing req string requirementString = [NSString stringWithFormat:@"anchor trusted and certificate leaf [subject.CN] = \"%@\"", SIGNING_AUTH]; @@ -68,11 +73,16 @@ pid_t clientPID = 0; //resume [newConnection resume]; + //grab client/requestor's pid + clientPID = audit_token_to_pid(((ExtendedNSXPCConnection*)newConnection).auditToken); + //happy shouldAccept = YES; - //grab client/requestor's pid - clientPID = audit_token_to_pid(((ExtendedNSXPCConnection*)newConnection).auditToken); + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"accepted new client connection (pid: %d)", clientPID]); + #endif //bail bail: @@ -110,7 +120,7 @@ int main(int argc, const char *argv[]) installExceptionHandlers(); //create the delegate for the service. - delegate = [ServiceDelegate new]; + delegate = [[ServiceDelegate alloc] init]; //set up the one NSXPCListener for this service // ->handles incoming connections @@ -126,7 +136,6 @@ int main(int argc, const char *argv[]) //happy status = 0; -//bail bail: return status; diff --git a/Shared/AboutWindow.xib b/Shared/AboutWindow.xib index f5d585f..110bc05 100644 --- a/Shared/AboutWindow.xib +++ b/Shared/AboutWindow.xib @@ -1,7 +1,7 @@ - + - + @@ -17,7 +17,7 @@ - + @@ -98,7 +98,7 @@ - + diff --git a/Shared/Consts.h b/Shared/Consts.h index 32185b7..7ff2be7 100644 --- a/Shared/Consts.h +++ b/Shared/Consts.h @@ -28,7 +28,7 @@ #define PRODUCT_URL @"https://objective-see.com/products/oversight.html" //product version url -#define PRODUCT_VERSION_URL @"https://objective-see.com/products.json" +#define PRODUCT_VERSIONS_URL @"https://objective-see.com/products.json" //patreon url #define PATREON_URL @"https://www.patreon.com/objective_see" @@ -129,9 +129,6 @@ //path to xattr #define XATTR @"/usr/bin/xattr" -//path to sudo -#define SUDO @"/usr/bin/sudo" - //path to facetime #define FACE_TIME @"/Applications/FaceTime.app/Contents/MacOS/FaceTime" @@ -187,5 +184,4 @@ //log to file flag #define LOG_TO_FILE 0x10 - #endif diff --git a/Shared/Exception.h b/Shared/Exception.h index 1f45004..53969b6 100755 --- a/Shared/Exception.h +++ b/Shared/Exception.h @@ -9,7 +9,7 @@ #import //install exception/signal handlers -void installExceptionHandlers(); +void installExceptionHandlers(void); //exception handler for Obj-C exceptions void exceptionHandler(NSException *exception); @@ -19,6 +19,3 @@ void signalHandler(int signal, siginfo_t *info, void *context); //display error window void displayErrorWindow(NSDictionary* errorInfo); - - - diff --git a/Shared/Logging.h b/Shared/Logging.h index c1eb394..e6f2dc9 100644 --- a/Shared/Logging.h +++ b/Shared/Logging.h @@ -13,15 +13,13 @@ void logMsg(int level, NSString* msg); //prep/open log file -BOOL initLogging(); +BOOL initLogging(void); //get path to log file -NSString* logFilePath(); +NSString* logFilePath(void); //de-init logging -void deinitLogging(); +void deinitLogging(void); //log to file void log2File(NSString* msg); - - diff --git a/Shared/Logging.m b/Shared/Logging.m index 568973d..bfa3d97 100644 --- a/Shared/Logging.m +++ b/Shared/Logging.m @@ -31,7 +31,7 @@ void logMsg(int level, NSString* msg) level &= ~LOG_TO_FILE; //alloc/init - // ->always start w/ 'OVERSIGHT' + pid + // ->always start w/ 'LULU' + pid logPrefix = [NSMutableString stringWithFormat:@"OVERSIGHT(%d)", getpid()]; //if its error, add error to prefix @@ -44,8 +44,8 @@ void logMsg(int level, NSString* msg) //debug mode logic #ifdef DEBUG - //in debug mode. promote debug msgs to LOG_NOTICE - // ->OS X/macOS only shows LOG_NOTICE and above in the system log + //in debug mode promote debug msgs to LOG_NOTICE + // OSX/macOS only shows LOG_NOTICE and above if(LOG_DEBUG == level) { //promote @@ -54,29 +54,23 @@ void logMsg(int level, NSString* msg) #endif - //log to syslog - syslog(level, "%s: %s", [logPrefix UTF8String], [msg UTF8String]); + //log to syslog if a level was specified + // as code doesn't use LOG_EMERG (0), this check is ok + if(0 != level) + { + //log to syslog + syslog(level, "%s: %s", [logPrefix UTF8String], [msg UTF8String]); + } //when a message is to be logged to file - // ->log it to file and syslog, when logging is enabled + // ->log it, when logging is enabled if(YES == shouldLog) { - //but only when logging is enabled + //but only when logging is enable if(nil != logFileHandle) { //log log2File(msg); - - //promote to notice for syslog - if(LOG_DEBUG == level) - { - //promote - level = LOG_NOTICE; - } - - //also syslog - // ->should result in 1 log msg, (in release), as all LOG_TO_FILE are at LOG_DEBUG level - syslog(level, "%s: %s", [logPrefix UTF8String], [msg UTF8String]); } } diff --git a/Shared/Utilities.h b/Shared/Utilities.h index 5a1f60b..290c81e 100644 --- a/Shared/Utilities.h +++ b/Shared/Utilities.h @@ -15,11 +15,11 @@ /* FUNCTIONS */ //get OS version -NSDictionary* getOSVersion(); +NSDictionary* getOSVersion(void); //get app's version // ->extracted from Info.plist -NSString* getAppVersion(); +NSString* getAppVersion(void); //set dir's|file's group/owner BOOL setFileOwner(NSString* path, NSNumber* groupID, NSNumber* ownerID, BOOL recursive); @@ -39,10 +39,10 @@ NSBundle* findAppBundle(NSString* binaryPath); //get app's version // ->extracted from Info.plist -NSString* getAppVersion(); +NSString* getAppVersion(void); //query interwebz to get latest version -NSString* getLatestVersion(); +NSString* getLatestVersion(void); //determine if there is a new version // -1, YES or NO @@ -72,7 +72,8 @@ void makeModal(NSWindowController* windowController); BOOL toggleLoginItem(NSURL* loginItem, int toggleFlag); //get logged in user -NSString* loggedinUser(); +// name, uid, and gid +NSMutableDictionary* loggedinUser(void); //find a process by name pid_t findProcess(NSString* processName); @@ -81,6 +82,9 @@ pid_t findProcess(NSString* processName); void makeTextViewHyperlink(NSTextField* textField, NSURL* url); //get active application -pid_t frontmostApplication(); +pid_t frontmostApplication(void); + +//check if process is alive +BOOL isProcessAlive(pid_t processID); #endif diff --git a/Shared/Utilities.m b/Shared/Utilities.m index 02f32ff..22e0ec1 100644 --- a/Shared/Utilities.m +++ b/Shared/Utilities.m @@ -274,17 +274,27 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait) //output NSData *output = nil; + //dispatch group + dispatch_group_t dispatchGroup = 0; + //init task - task = [NSTask new]; + task = [[NSTask alloc] init]; //init pipe outPipe = [NSPipe pipe]; + //create dispatch group + dispatchGroup = dispatch_group_create(); + //set task's path task.launchPath = binaryPath; //set task's args - task.arguments = arguments; + if(nil != arguments) + { + //add + task.arguments = arguments; + } //set task's output to pipe // ->but only if we're waiting for exit @@ -299,6 +309,16 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait) logMsg(LOG_DEBUG, [NSString stringWithFormat:@"@exec'ing %@ (args: %@)", binaryPath, arguments]); #endif + //enter dispatch + dispatch_group_enter(dispatchGroup); + + //set task's termination to leave dispatch group + task.terminationHandler = ^(NSTask *task){ + + //leave + dispatch_group_leave(dispatchGroup); + }; + //wrap task launch @try { @@ -333,7 +353,7 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait) #endif //wait till exit - [task waitUntilExit]; + dispatch_group_wait(dispatchGroup, DISPATCH_TIME_FOREVER); //dbg msg #ifdef DEBUG @@ -345,6 +365,7 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait) //bail bail: + return output; } @@ -756,59 +777,54 @@ bail: //query interwebz to get latest version NSString* getLatestVersion() { - //version data - __block NSData* versionData = nil; + //product version(s) data + NSData* productsVersionData = nil; //version dictionary - NSDictionary* versionDictionary = nil; + NSDictionary* productsVersionDictionary = nil; //latest version NSString* latestVersion = nil; - //run in background if main thread - if(YES == [NSThread isMainThread]) - { - //run in background - dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), - ^{ - //get version data - versionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSION_URL]]; - }); - } - //no need to background - else - { - //get version from remote URL - versionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSION_URL]]; - } - - //sanity check - if(nil == versionData) + //get version from remote URL + productsVersionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSIONS_URL]]; + if(nil == productsVersionData) { //bail goto bail; } //convert JSON to dictionary - versionDictionary = [NSJSONSerialization JSONObjectWithData:versionData options:0 error:nil]; - - //sanity check - if(nil == versionDictionary) + // ->wrap as may throw exception + @try + { + //convert + productsVersionDictionary = [NSJSONSerialization JSONObjectWithData:productsVersionData options:0 error:nil]; + if(nil == productsVersionDictionary) + { + //bail + goto bail; + } + } + @catch(NSException* exception) { //bail goto bail; } //extract latest version - latestVersion = versionDictionary[@"latestVersion"]; + latestVersion = [[productsVersionDictionary objectForKey:@"OverSight"] objectForKey:@"version"]; + + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"latest version: %@", latestVersion]); + #endif -//bail bail: return latestVersion; } - //wait until a window is non nil // ->then make it modal void makeModal(NSWindowController* windowController) @@ -981,14 +997,27 @@ bail: } //get logged in user -NSString* loggedinUser() +// name, uid, and gid +NSMutableDictionary* loggedinUser() { + //user info + NSMutableDictionary* userInfo = nil; + //store SCDynamicStoreRef store = nil; //user NSString* user = nil; + //uid + uid_t uid = 0; + + //gid + gid_t gid = 0; + + //allco dictionary + userInfo = [NSMutableDictionary dictionary]; + //create store store = SCDynamicStoreCreate(NULL, CFSTR("GetConsoleUser"), NULL, NULL); if(NULL == store) @@ -997,8 +1026,17 @@ NSString* loggedinUser() goto bail; } - //get user - user = CFBridgingRelease(SCDynamicStoreCopyConsoleUser(store, NULL, NULL)); + //get user and uid/gid + user = CFBridgingRelease(SCDynamicStoreCopyConsoleUser(store, &uid, &gid)); + + //add user + userInfo[@"user"] = user; + + //add uid + userInfo[@"uid"] = [NSNumber numberWithUnsignedInt:uid]; + + //add uid + userInfo[@"gid"] = [NSNumber numberWithUnsignedInt:gid]; //bail bail: @@ -1010,7 +1048,7 @@ bail: CFRelease(store); } - return user; + return userInfo; } //find a process by name @@ -1059,7 +1097,7 @@ pid_t findProcess(NSString* processName) //get name processPath = getProcessPath(pids[i]); if( (nil == processPath) || - (0 == processPath.length) ) + (0 == processPath.length) ) { //skip continue; @@ -1134,3 +1172,31 @@ pid_t frontmostApplication() return NSWorkspace.sharedWorkspace.frontmostApplication.processIdentifier; } +//check if process is alive +BOOL isProcessAlive(pid_t processID) +{ + //ret var + BOOL bIsAlive = NO; + + //signal status + int signalStatus = -1; + + //send kill with 0 to determine if alive + // -> see: http://stackoverflow.com/questions/9152979/check-if-process-exists-given-its-pid + signalStatus = kill(processID, 0); + + //is alive? + if( (0 == signalStatus) || + ( (0 != signalStatus) && (errno != ESRCH) ) ) + { + //dbg msg + #ifdef DEBUG + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"agent (%d) is ALIVE", processID]); + #endif + + //alive! + bIsAlive = YES; + } + + return bIsAlive; +} diff --git a/Shared/XPCProtocol.h b/Shared/XPCProtocol.h index 5f37554..701df1d 100644 --- a/Shared/XPCProtocol.h +++ b/Shared/XPCProtocol.h @@ -14,6 +14,10 @@ //start enumerator -(void)initialize:(void (^)(void))reply; +//heartbeat +// need as otherwise kernel might kill XPC +-(void)heartBeat:(void (^)(BOOL))reply; + //get (new) audio procs -(void)getAudioProcs:(void (^)(NSMutableArray *))reply; diff --git a/contributors.txt b/contributors.txt new file mode 100644 index 0000000..a8f8973 --- /dev/null +++ b/contributors.txt @@ -0,0 +1 @@ +patrick wardle