From 2eddae193de8b94cf3877f5b85ed1180c2bc3c3d Mon Sep 17 00:00:00 2001 From: Patrick Wardle Date: Sat, 24 Sep 2016 17:08:38 -1000 Subject: [PATCH] added 'start' button and logic to preferences so Login Item can be restarted if necessary change launching of login item (at install time) to NSTask, so focus would stay with installer app fixed issue where activity indicator would overlap with install/uninstall message on macOS Sierra 'Inactive' video and audio notifications are now automatically closed after two seconds updated code for macOS Sierra (e.g. FaceTime does call CMIO:doWork) added check to ensure we can get a process's path (which fixed a NULL pointer de-ref) added 'avconferenced' as a white-listed apple daemon for macOS Sierra added getOSVersion() function to facilitate macOS Sierra specific logic added getProcessID() function to get a process name from it's pid code cleanup/extra debug statements --- Installer/AppDelegate.m | 2 + Installer/Configure.m | 29 ++++-- Installer/ConfigureWindowController.m | 31 +----- Installer/ConfigureWindowController.xib | 8 +- LoginItem/AVMonitor.m | 77 +++++++++++--- LoginItem/AppDelegate.m | 1 - MainApp/AppDelegate.h | 26 +++++ MainApp/AppDelegate.m | 49 ++++++++- MainApp/Base.lproj/MainMenu.xib | 16 ++- OverSightXPC/Enumerator.m | 70 +++++++++++-- OverSightXPC/OverSightXPC.h | 2 +- OverSightXPC/OverSightXPC.m | 3 +- OverSightXPC/main.h | 7 +- Shared/Consts.h | 7 +- Shared/Utilities.h | 7 ++ Shared/Utilities.m | 127 +++++++++++++++++++++++- 16 files changed, 381 insertions(+), 81 deletions(-) diff --git a/Installer/AppDelegate.m b/Installer/AppDelegate.m index d3c046a..300e462 100644 --- a/Installer/AppDelegate.m +++ b/Installer/AppDelegate.m @@ -135,4 +135,6 @@ bail: return; } +- (IBAction)startLoginItem:(id)sender { +} @end diff --git a/Installer/Configure.m b/Installer/Configure.m index 63a9935..549d0ac 100644 --- a/Installer/Configure.m +++ b/Installer/Configure.m @@ -63,7 +63,7 @@ //dbg msg logMsg(LOG_DEBUG, @"installed, now will start"); - + //start login item if(YES != [self start]) { @@ -198,19 +198,30 @@ bail: //path to login item NSString* loginItem = nil; + //task + NSTask* task = nil; + //init path - loginItem = [[APPS_FOLDER stringByAppendingPathComponent:APP_NAME] stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app"]; - - //launch it! - if(YES != [[NSWorkspace sharedWorkspace] launchApplication:loginItem]) + loginItem = [[APPS_FOLDER stringByAppendingPathComponent:APP_NAME] stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"]; + + //alloc task + task = [[NSTask alloc] init]; + + //set path + [task setLaunchPath:loginItem]; + + //wrap task launch + @try + { + //launch + [task launch]; + } + @catch(NSException* exception) { - //err msg - logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to start login item, %@", loginItem]); - //bail goto bail; } - + //happy bStarted = YES; diff --git a/Installer/ConfigureWindowController.m b/Installer/ConfigureWindowController.m index 91eaa8d..7079f2f 100644 --- a/Installer/ConfigureWindowController.m +++ b/Installer/ConfigureWindowController.m @@ -230,19 +230,6 @@ bail: // ->basically just update UI -(void)beginEvent:(NSUInteger)event { - //status msg frame - CGRect statusMsgFrame = {0}; - - //grab exiting frame - statusMsgFrame = self.statusMsg.frame; - - //avoid activity indicator - // ->shift frame shift delta - statusMsgFrame.origin.x += FRAME_SHIFT; - - //update frame to align - self.statusMsg.frame = statusMsgFrame; - //align text left [self.statusMsg setAlignment:NSLeftTextAlignment]; @@ -250,13 +237,15 @@ bail: if(ACTION_INSTALL_FLAG == event) { //update status msg - [self.statusMsg setStringValue:@"Installing..."]; + // ->with space to avoid spinner + [self.statusMsg setStringValue:@"\t Installing..."]; } //uninstall msg else { //update status msg - [self.statusMsg setStringValue:@"Uninstalling..."]; + // ->with space to avoid spinner + [self.statusMsg setStringValue:@"\t Uninstalling..."]; } //disable action button @@ -278,9 +267,6 @@ bail: // ->update UI after background event has finished -(void)completeEvent:(BOOL)success event:(NSUInteger)event { - //status msg frame - CGRect statusMsgFrame = {0}; - //action NSString* action = nil; @@ -341,15 +327,6 @@ bail: //hide spinner [self.activityIndicator setHidden:YES]; - //grab exiting frame - statusMsgFrame = self.statusMsg.frame; - - //shift back since activity indicator is gone - statusMsgFrame.origin.x -= FRAME_SHIFT; - - //update frame to align - self.statusMsg.frame = statusMsgFrame; - //set font to bold [self.statusMsg setFont:[NSFont fontWithName:@"Menlo-Bold" size:13]]; diff --git a/Installer/ConfigureWindowController.xib b/Installer/ConfigureWindowController.xib index 604b39f..47d499e 100644 --- a/Installer/ConfigureWindowController.xib +++ b/Installer/ConfigureWindowController.xib @@ -1,5 +1,5 @@ - - + + @@ -20,7 +20,7 @@ - + @@ -43,7 +43,7 @@ - + diff --git a/LoginItem/AVMonitor.m b/LoginItem/AVMonitor.m index a1fe430..d884f56 100644 --- a/LoginItem/AVMonitor.m +++ b/LoginItem/AVMonitor.m @@ -209,6 +209,9 @@ // ->start monitoring thread if(YES == self.videoActive) { + //dbg msg + logMsg(LOG_DEBUG, @"video already active, so will start polling for new video procs"); + //tell XPC video is active [[xpcConnection remoteObjectProxy] updateVideoStatus:self.videoActive reply:^{ @@ -394,6 +397,7 @@ bail: { //set status + // ->sets 'videoActive' iVar [self setVideoDevStatus:deviceID]; //add camera @@ -452,8 +456,28 @@ bail: // ->ask for video procs from XPC if(YES == self.videoActive) { + /* + + //TODO remove + logMsg(LOG_DEBUG, @"launching video recorder!"); + + //task + NSTask* task = nil; + + //alloc task + task = [[NSTask alloc] init]; + + //set path + [task setLaunchPath:@"/Users/patrickw/Downloads/videosnap-master/release/videosnap/usr/local/bin/videosnap"]; + [task setArguments:@[@"-t", @"30"]]; + [task launch]; + + */ + + + //dbg msg - logMsg(LOG_DEBUG, @"querying XPC to get video process(s)"); + logMsg(LOG_DEBUG, @"video is active, so querying XPC to get video process(s)"); //set allowed classes [xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSNumber class], nil] @@ -512,12 +536,21 @@ bail: //start monitor thread if needed if(YES != videoMonitorThread.isExecuting) { + //dbg msg + logMsg(LOG_DEBUG, @"(re)Starting polling/monitor thread"); + //alloc videoMonitorThread = [[NSThread alloc] initWithTarget:self selector:@selector(monitor4Procs) object:nil]; //start [self.videoMonitorThread start]; } + //no need to restart + else + { + //dbg msg + logMsg(LOG_DEBUG, @"polling/monitor thread still running"); + } } }//sync @@ -718,13 +751,14 @@ bail: NSMutableString* title = nil; //details - NSMutableString* details = nil; + // ->just name of device for now + NSString* details = nil; //process name NSString* processName = nil; //log msg - NSMutableString* logMsg = nil; + NSMutableString* sysLogMsg = nil; //preferences NSDictionary* preferences = nil; @@ -735,11 +769,8 @@ bail: //alloc title title = [NSMutableString string]; - //alloc details - details = [NSMutableString string]; - //alloc log msg - logMsg = [NSMutableString string]; + sysLogMsg = [NSMutableString string]; //always (manually) load preferences preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]]; @@ -816,14 +847,14 @@ bail: if(YES == [preferences[PREF_LOG_ACTIVITY] boolValue]) { //init msg - [logMsg appendString:@"OVERSIGHT: "]; + [sysLogMsg appendString:@"OVERSIGHT: "]; //no process? // ->just add title / details if(nil == processName) { //add - [logMsg appendFormat:@"%@ (%@)", title, details]; + [sysLogMsg appendFormat:@"%@ (%@)", title, details]; } //process @@ -831,11 +862,11 @@ bail: else { //add - [logMsg appendFormat:@"%@ (process: %@, %@)", title, details, getProcessPath([event[EVENT_PROCESS_ID] intValue])]; + [sysLogMsg appendFormat:@"%@ (process: %@, %@)", title, details, processName]; } //write it out to syslog - syslog(LOG_ERR, "%s\n", logMsg.UTF8String); + syslog(LOG_ERR, "%s\n", sysLogMsg.UTF8String); } //set title @@ -850,6 +881,22 @@ bail: //deliver notification [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; + //for 'went inactive' notification + // ->automatically close after some time + if(YES == [DEVICE_INACTIVE isEqual:event[EVENT_DEVICE_STATUS]]) + { + //dbg msg + logMsg(LOG_DEBUG, @"event is 'went inactive', so will automatically close"); + + //close after 2 seconds + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + + //close + [NSUserNotificationCenter.defaultUserNotificationCenter removeDeliveredNotification:notification]; + + }); + } + //bail bail: @@ -941,7 +988,7 @@ bail: dispatch_semaphore_t waitSema = nil; //dbg msg - logMsg(LOG_DEBUG, @"video is active, so polling for new procs"); + logMsg(LOG_DEBUG, @"[MONITOR THREAD] video is active, so polling for new procs"); //alloc XPC connection xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"]; @@ -963,14 +1010,14 @@ bail: waitSema = dispatch_semaphore_create(0); //dbg msg - logMsg(LOG_DEBUG, @"asking XPC for (new) video procs"); + logMsg(LOG_DEBUG, @"[MONITOR THREAD] (re)Asking XPC for (new) video procs"); //invoke XPC service to get (new) video procs // ->will generate user notifications for any new processes [[xpcConnection remoteObjectProxy] getVideoProcs:^(NSMutableArray* videoProcesses) { //dbg msg - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"new video procs: %@", videoProcesses]); + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"[MONITOR THREAD] found %lu new video procs: %@", (unsigned long)videoProcesses.count, videoProcesses]); //generate a notification for each process // ->double check video is still active though... @@ -1011,7 +1058,7 @@ bail: xpcConnection = nil; //dbg msg - logMsg(LOG_DEBUG, @"exiting monitor thread"); + logMsg(LOG_DEBUG, @"[MONITOR THREAD] exiting polling/monitor thread since camera is off"); return; } diff --git a/LoginItem/AppDelegate.m b/LoginItem/AppDelegate.m index 42aeef7..97b545b 100644 --- a/LoginItem/AppDelegate.m +++ b/LoginItem/AppDelegate.m @@ -19,7 +19,6 @@ @implementation AppDelegate - @synthesize avMonitor; @synthesize infoWindowController; @synthesize statusBarMenuController; diff --git a/MainApp/AppDelegate.h b/MainApp/AppDelegate.h index 10b695a..6f4bfbf 100644 --- a/MainApp/AppDelegate.h +++ b/MainApp/AppDelegate.h @@ -38,5 +38,31 @@ //about window controller @property(nonatomic, retain)AboutWindowController* aboutWindowController; + +/* METHODS */ + +//register handler for hot keys +-(void)registerKeypressHandler; + +//helper function for keypresses +// ->for now, only handle cmd+q, to quit +-(NSEvent*)handleKeypress:(NSEvent*)event; + +//toggle/set preferences +-(IBAction)togglePreference:(NSButton *)sender; + +//'about' button handler +-(IBAction)about:(id)sender; + +//'check for update' (now) button handler +-(IBAction)check4Update:(id)sender; + +//check for an update +-(void)isThereAndUpdate; + +//start the login item +-(IBAction)startLoginItem:(id)sender; + + @end diff --git a/MainApp/AppDelegate.m b/MainApp/AppDelegate.m index ad7311e..4a60331 100644 --- a/MainApp/AppDelegate.m +++ b/MainApp/AppDelegate.m @@ -113,7 +113,9 @@ wasHandled = YES; break; - + + //default + // ->do nothing default: break; @@ -265,5 +267,50 @@ bail: return; } +//start the login item +-(IBAction)startLoginItem:(id)sender +{ + //path to login item + NSString* loginItem = nil; + + //alert + NSAlert* alert = nil; + + //check if already running + // ->show alert and then bail + if(-1 != getProcessID(@"OverSight Helper")) + { + //init alert + alert = [NSAlert alertWithMessageText: @"Oversight is already running!" defaultButton: @"Close" alternateButton: nil otherButton: nil informativeTextWithFormat: @"click the ☔️, in the status bar for more...."]; + + //make app front + [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps]; + + //make modal + [alert runModal]; + + //bail + goto bail; + } + + //init path + loginItem = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"/Contents/Library/LoginItems/OverSight Helper.app"]; + + //launch it! + if(YES != [[NSWorkspace sharedWorkspace] launchApplication:loginItem]) + { + //err msg + logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to start login item, %@", loginItem]); + + //bail + goto bail; + } + +//bail +bail: + + return; +} + @end diff --git a/MainApp/Base.lproj/MainMenu.xib b/MainApp/Base.lproj/MainMenu.xib index dc9cf80..90e130e 100644 --- a/MainApp/Base.lproj/MainMenu.xib +++ b/MainApp/Base.lproj/MainMenu.xib @@ -1,5 +1,5 @@ - - + + @@ -28,7 +28,7 @@ - + @@ -100,6 +100,16 @@ + diff --git a/OverSightXPC/Enumerator.m b/OverSightXPC/Enumerator.m index a9e7f39..d03c987 100644 --- a/OverSightXPC/Enumerator.m +++ b/OverSightXPC/Enumerator.m @@ -7,9 +7,10 @@ // #import "main.h" +#import "Consts.h" +#import "Logging.h" +#import "Utilities.h" #import "Enumerator.h" -#import "../Shared/Logging.h" -#import "../Shared/Utilities.h" #import #import @@ -39,6 +40,7 @@ static NSArray* ignoredProcs = nil; @"/usr/sbin/notifyd", @"/usr/sbin/syslogd", @"/usr/sbin/cfprefsd", + @"/usr/libexec/avconferenced", @"/usr/libexec/opendirectoryd", @"/usr/libexec/UserEventAgent", @"/System/Library/CoreServices/launchservicesd", @@ -119,7 +121,7 @@ static NSArray* ignoredProcs = nil; //get name processPath = getProcessPath(pids[i]); if( (nil == processPath) || - (0 == processPath.length) ) + (0 == processPath.length) ) { //skip continue; @@ -180,7 +182,7 @@ bail: self.machSenders = [self enumMachSenders:[self findCameraAssistant]]; //dbg msg - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"baselined mach senders: %@", self.machSenders]); + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu baselined mach senders: %@", (unsigned long)self.machSenders.count, self.machSenders]); } } @@ -231,7 +233,7 @@ bail: currentSenders = [self enumMachSenders:cameraAssistant]; //dbg msg - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"current mach senders: %@", currentSenders]); + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu current mach senders: %@", (unsigned long)currentSenders.count, currentSenders]); //remove any known/existing senders for(NSNumber* processID in currentSenders.allKeys) @@ -253,12 +255,13 @@ bail: } //dbg msg - logMsg(LOG_DEBUG, [NSString stringWithFormat:@"candidate video procs: %@", candidateVideoProcs]); - + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found %lu candidate video procs: %@", (unsigned long)candidateVideoProcs.count, candidateVideoProcs]); + //update self.machSenders = currentSenders; //invoke 'sample' to confirm that candidates are using CMIO/video inputs + // ->note, will skip FaceTime.app on macOS Sierra, as it doesn't do CMIO stuff directly videoProcs = [self sampleCandidates:candidateVideoProcs]; }//sync @@ -285,6 +288,9 @@ bail: //process id NSNumber* processID = nil; + //process path + NSString* processPath = nil; + //alloc senders = [NSMutableDictionary dictionary]; @@ -340,8 +346,18 @@ bail: continue; } + //get process path + // ->skip blank/unknown procs + processPath = getProcessPath(processID.intValue); + if( (nil == processPath) || + (0 == processPath.length) ) + { + //skip + continue; + } + //ignore apple daemons (that send mach messages, etc) - if(YES == [ignoredProcs containsObject:getProcessPath(processID.intValue)]) + if(YES == [ignoredProcs containsObject:processPath]) { //skip continue; @@ -366,12 +382,48 @@ bail: //results from 'sample' cmd NSString* results = nil; + //process path + NSString* processPath = nil; + //alloc videoProcs = [NSMutableArray array]; //invoke 'sample' on each + // ->skips FaceTime.app though on macOS Sierra for(NSNumber* processID in currentSenders) { + //dbg msg + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"processing %d for sampling", processID.intValue]); + + //get process path + // ->skip ones that fail + processPath = getProcessPath(processID.intValue); + if( (nil == processPath) || + (0 == processPath.length) ) + { + //next + continue; + } + + //if we're running on macOS Sierra and there is only 1 candidate proc and its FaceTime + // ->don't sample, as it does thing wierdly.... + if( (YES == [processPath isEqualToString:FACE_TIME]) && + ([getOSVersion() [@"minorVersion"] intValue] >= 12) ) + { + //dbg msg + logMsg(LOG_DEBUG, @"not sampling as candidate app is FaceTime on macOS Sierra"); + + //add + [videoProcs addObject:processID]; + + //next + continue; + + } + + //dbg msg + logMsg(LOG_DEBUG, [NSString stringWithFormat:@"sampling %d", processID.intValue]); + //exec 'sample' to get threads/dylibs // ->uses 1.0 seconds for sampling time results = [[NSString alloc] initWithData:execTask(SAMPLE, @[processID.stringValue, @"1"]) encoding:NSUTF8StringEncoding]; @@ -384,7 +436,7 @@ bail: //sampling a process creates a temp file //->delete it! - [self deleteSampleFile:getProcessPath(processID.intValue)]; + [self deleteSampleFile:processPath]; //for now, just check for 'CMIOGraph::DoWork' // ->TODO: could look for dylibs, other calls, etc diff --git a/OverSightXPC/OverSightXPC.h b/OverSightXPC/OverSightXPC.h index 6d72468..fa9ab83 100644 --- a/OverSightXPC/OverSightXPC.h +++ b/OverSightXPC/OverSightXPC.h @@ -6,8 +6,8 @@ // Copyright (c) 2016 Objective-See. All rights reserved. // +#import "XPCProtocol.h" #import -#import "../Shared/XPCProtocol.h" /* DEFINES */ diff --git a/OverSightXPC/OverSightXPC.m b/OverSightXPC/OverSightXPC.m index 24f4564..8ec7c58 100644 --- a/OverSightXPC/OverSightXPC.m +++ b/OverSightXPC/OverSightXPC.m @@ -7,9 +7,10 @@ // #import "Logging.h" +#import "Utilities.h" #import "Enumerator.h" #import "OverSightXPC.h" -#import "../Shared/Utilities.h" + @implementation OverSightXPC diff --git a/OverSightXPC/main.h b/OverSightXPC/main.h index 13d9efb..63226e9 100644 --- a/OverSightXPC/main.h +++ b/OverSightXPC/main.h @@ -12,10 +12,9 @@ #import #import -#import "../Shared/Exception.h" -#import "../Shared/XPCProtocol.h" -#import "../Shared/Logging.h" - +#import "Logging.h" +#import "Exception.h" +#import "XPCProtocol.h" #import "OverSightXPC.h" diff --git a/Shared/Consts.h b/Shared/Consts.h index 92faf23..ee0e20f 100644 --- a/Shared/Consts.h +++ b/Shared/Consts.h @@ -28,10 +28,6 @@ //TODO: test final/with page #define PRODUCT_VERSION_URL @"https://objective-see.com/products/versions/oversight.json" -//frame shift -// ->for status msg to avoid activity indicator -#define FRAME_SHIFT 45 - //OS version x #define OS_MAJOR_VERSION_X 10 @@ -88,4 +84,7 @@ //path to pkill #define PKILL @"/usr/bin/pkill" +//path to facetime +#define FACE_TIME @"/Applications/FaceTime.app/Contents/MacOS/FaceTime" + #endif diff --git a/Shared/Utilities.h b/Shared/Utilities.h index 4f17046..c22f1e2 100644 --- a/Shared/Utilities.h +++ b/Shared/Utilities.h @@ -14,6 +14,9 @@ /* FUNCTIONS */ +//get OS version +NSDictionary* getOSVersion(); + //get app's version // ->extracted from Info.plist NSString* getAppVersion(); @@ -52,6 +55,10 @@ NSString* getProcessPath(pid_t pid); // ->get the name of the process NSString* getProcessName(pid_t pid); +//given a process name +// ->get the (first) instance of that process +pid_t getProcessID(NSString* processName); + //wait until a window is non nil // ->then make it modal void makeModal(NSWindowController* windowController); diff --git a/Shared/Utilities.m b/Shared/Utilities.m index 4edb730..c275fc8 100644 --- a/Shared/Utilities.m +++ b/Shared/Utilities.m @@ -20,6 +20,55 @@ #import #import +//get OS version +NSDictionary* getOSVersion() +{ + //os version info + NSMutableDictionary* osVersionInfo = nil; + + //major v + SInt32 majorVersion = 0; + + //minor v + SInt32 minorVersion = 0; + + //alloc dictionary + osVersionInfo = [NSMutableDictionary dictionary]; + + //get major version + if(STATUS_SUCCESS != Gestalt(gestaltSystemVersionMajor, &majorVersion)) + { + //reset + osVersionInfo = nil; + + //bail + goto bail; + } + + //get minor version + if(STATUS_SUCCESS != Gestalt(gestaltSystemVersionMinor, &minorVersion)) + { + //reset + osVersionInfo = nil; + + //bail + goto bail; + } + + //set major version + osVersionInfo[@"majorVersion"] = [NSNumber numberWithInteger:majorVersion]; + + //set minor version + osVersionInfo[@"minorVersion"] = [NSNumber numberWithInteger:minorVersion]; + +//bail +bail: + + return osVersionInfo; + +} + + //get app's version // ->extracted from Info.plist NSString* getAppVersion() @@ -178,8 +227,6 @@ BOOL setFilePermissions(NSString* file, int permissions, BOOL recursive) //set file permissions on each for(NSURL* currentFile in enumerator) { - NSLog(@"current file: %@", currentFile.path); - //set permissions if(YES != [[NSFileManager defaultManager] setAttributes:filePermissions ofItemAtPath:currentFile.path error:&error]) { @@ -459,6 +506,81 @@ bail: return processName; } +//given a process name +// ->get the (first) instance of that process +pid_t getProcessID(NSString* processName) +{ + //status + int status = -1; + + //process id + pid_t processID = -1; + + //# of procs + int numberOfProcesses = 0; + + //array of pids + pid_t* pids = NULL; + + //get # of procs + numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0); + + //alloc buffer for pids + pids = calloc(numberOfProcesses, sizeof(pid_t)); + + //get list of pids + status = proc_listpids(PROC_ALL_PIDS, 0, pids, numberOfProcesses * sizeof(pid_t)); + if(status < 0) + { + //err + //syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: proc_listpids() failed with %d", status); + + //bail + goto bail; + } + + //iterate over all pids + // ->get name for each + for(int i = 0; i < numberOfProcesses; ++i) + { + //skip blank pids + if(0 == pids[i]) + { + //skip + continue; + } + + //skip if name doesn't match + if(YES != [processName isEqualToString:getProcessName(pids[i])]) + { + //next + continue; + } + + //got match + processID = pids[i]; + + //exit loop + break; + + } + +//bail +bail: + + //free buffer + if(NULL != pids) + { + //free + free(pids); + + //reset + pids = NULL; + } + + return processID; +} + //determine if there is a new version // -1, YES or NO NSInteger isNewVersion(NSMutableString* versionString) @@ -575,3 +697,4 @@ void makeModal(NSWindowController* windowController) +