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
This commit is contained in:
parent
d9ae15b3bc
commit
2eddae193d
|
@ -135,4 +135,6 @@ bail:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (IBAction)startLoginItem:(id)sender {
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
|
|
||||||
//dbg msg
|
//dbg msg
|
||||||
logMsg(LOG_DEBUG, @"installed, now will start");
|
logMsg(LOG_DEBUG, @"installed, now will start");
|
||||||
|
|
||||||
//start login item
|
//start login item
|
||||||
if(YES != [self start])
|
if(YES != [self start])
|
||||||
{
|
{
|
||||||
|
@ -198,19 +198,30 @@ bail:
|
||||||
//path to login item
|
//path to login item
|
||||||
NSString* loginItem = nil;
|
NSString* loginItem = nil;
|
||||||
|
|
||||||
|
//task
|
||||||
|
NSTask* task = nil;
|
||||||
|
|
||||||
//init path
|
//init path
|
||||||
loginItem = [[APPS_FOLDER stringByAppendingPathComponent:APP_NAME] stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app"];
|
loginItem = [[APPS_FOLDER stringByAppendingPathComponent:APP_NAME] stringByAppendingPathComponent:@"Contents/Library/LoginItems/OverSight Helper.app/Contents/MacOS/OverSight Helper"];
|
||||||
|
|
||||||
//launch it!
|
//alloc task
|
||||||
if(YES != [[NSWorkspace sharedWorkspace] launchApplication:loginItem])
|
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
|
//bail
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
//happy
|
//happy
|
||||||
bStarted = YES;
|
bStarted = YES;
|
||||||
|
|
||||||
|
|
|
@ -230,19 +230,6 @@ bail:
|
||||||
// ->basically just update UI
|
// ->basically just update UI
|
||||||
-(void)beginEvent:(NSUInteger)event
|
-(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
|
//align text left
|
||||||
[self.statusMsg setAlignment:NSLeftTextAlignment];
|
[self.statusMsg setAlignment:NSLeftTextAlignment];
|
||||||
|
|
||||||
|
@ -250,13 +237,15 @@ bail:
|
||||||
if(ACTION_INSTALL_FLAG == event)
|
if(ACTION_INSTALL_FLAG == event)
|
||||||
{
|
{
|
||||||
//update status msg
|
//update status msg
|
||||||
[self.statusMsg setStringValue:@"Installing..."];
|
// ->with space to avoid spinner
|
||||||
|
[self.statusMsg setStringValue:@"\t Installing..."];
|
||||||
}
|
}
|
||||||
//uninstall msg
|
//uninstall msg
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//update status msg
|
//update status msg
|
||||||
[self.statusMsg setStringValue:@"Uninstalling..."];
|
// ->with space to avoid spinner
|
||||||
|
[self.statusMsg setStringValue:@"\t Uninstalling..."];
|
||||||
}
|
}
|
||||||
|
|
||||||
//disable action button
|
//disable action button
|
||||||
|
@ -278,9 +267,6 @@ bail:
|
||||||
// ->update UI after background event has finished
|
// ->update UI after background event has finished
|
||||||
-(void)completeEvent:(BOOL)success event:(NSUInteger)event
|
-(void)completeEvent:(BOOL)success event:(NSUInteger)event
|
||||||
{
|
{
|
||||||
//status msg frame
|
|
||||||
CGRect statusMsgFrame = {0};
|
|
||||||
|
|
||||||
//action
|
//action
|
||||||
NSString* action = nil;
|
NSString* action = nil;
|
||||||
|
|
||||||
|
@ -341,15 +327,6 @@ bail:
|
||||||
//hide spinner
|
//hide spinner
|
||||||
[self.activityIndicator setHidden:YES];
|
[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
|
//set font to bold
|
||||||
[self.statusMsg setFont:[NSFont fontWithName:@"Menlo-Bold" size:13]];
|
[self.statusMsg setFont:[NSFont fontWithName:@"Menlo-Bold" size:13]];
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16A323" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="196" y="240" width="460" height="176"/>
|
<rect key="contentRect" x="196" y="240" width="460" height="176"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
|
||||||
<view key="contentView" id="se5-gp-TjO">
|
<view key="contentView" id="se5-gp-TjO">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="460" height="176"/>
|
<rect key="frame" x="0.0" y="0.0" width="460" height="176"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon" id="bCU-0f-ff8"/>
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon" id="bCU-0f-ff8"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" preferredMaxLayoutWidth="293" translatesAutoresizingMaskIntoConstraints="NO" id="SpB-Xc-WlB">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" preferredMaxLayoutWidth="293" translatesAutoresizingMaskIntoConstraints="NO" id="SpB-Xc-WlB">
|
||||||
<rect key="frame" x="142" y="10" width="297" height="47"/>
|
<rect key="frame" x="144" y="11" width="293" height="47"/>
|
||||||
<textFieldCell key="cell" truncatesLastVisibleLine="YES" enabled="NO" sendsActionOnEndEditing="YES" alignment="center" id="Rib-WU-Syl">
|
<textFieldCell key="cell" truncatesLastVisibleLine="YES" enabled="NO" sendsActionOnEndEditing="YES" alignment="center" id="Rib-WU-Syl">
|
||||||
<font key="font" size="13" name="Menlo-Regular"/>
|
<font key="font" size="13" name="Menlo-Regular"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
|
|
@ -209,6 +209,9 @@
|
||||||
// ->start monitoring thread
|
// ->start monitoring thread
|
||||||
if(YES == self.videoActive)
|
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
|
//tell XPC video is active
|
||||||
[[xpcConnection remoteObjectProxy] updateVideoStatus:self.videoActive reply:^{
|
[[xpcConnection remoteObjectProxy] updateVideoStatus:self.videoActive reply:^{
|
||||||
|
|
||||||
|
@ -394,6 +397,7 @@ bail:
|
||||||
{
|
{
|
||||||
|
|
||||||
//set status
|
//set status
|
||||||
|
// ->sets 'videoActive' iVar
|
||||||
[self setVideoDevStatus:deviceID];
|
[self setVideoDevStatus:deviceID];
|
||||||
|
|
||||||
//add camera
|
//add camera
|
||||||
|
@ -452,8 +456,28 @@ bail:
|
||||||
// ->ask for video procs from XPC
|
// ->ask for video procs from XPC
|
||||||
if(YES == self.videoActive)
|
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
|
//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
|
//set allowed classes
|
||||||
[xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSNumber class], nil]
|
[xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSNumber class], nil]
|
||||||
|
@ -512,12 +536,21 @@ bail:
|
||||||
//start monitor thread if needed
|
//start monitor thread if needed
|
||||||
if(YES != videoMonitorThread.isExecuting)
|
if(YES != videoMonitorThread.isExecuting)
|
||||||
{
|
{
|
||||||
|
//dbg msg
|
||||||
|
logMsg(LOG_DEBUG, @"(re)Starting polling/monitor thread");
|
||||||
|
|
||||||
//alloc
|
//alloc
|
||||||
videoMonitorThread = [[NSThread alloc] initWithTarget:self selector:@selector(monitor4Procs) object:nil];
|
videoMonitorThread = [[NSThread alloc] initWithTarget:self selector:@selector(monitor4Procs) object:nil];
|
||||||
|
|
||||||
//start
|
//start
|
||||||
[self.videoMonitorThread start];
|
[self.videoMonitorThread start];
|
||||||
}
|
}
|
||||||
|
//no need to restart
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//dbg msg
|
||||||
|
logMsg(LOG_DEBUG, @"polling/monitor thread still running");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}//sync
|
}//sync
|
||||||
|
@ -718,13 +751,14 @@ bail:
|
||||||
NSMutableString* title = nil;
|
NSMutableString* title = nil;
|
||||||
|
|
||||||
//details
|
//details
|
||||||
NSMutableString* details = nil;
|
// ->just name of device for now
|
||||||
|
NSString* details = nil;
|
||||||
|
|
||||||
//process name
|
//process name
|
||||||
NSString* processName = nil;
|
NSString* processName = nil;
|
||||||
|
|
||||||
//log msg
|
//log msg
|
||||||
NSMutableString* logMsg = nil;
|
NSMutableString* sysLogMsg = nil;
|
||||||
|
|
||||||
//preferences
|
//preferences
|
||||||
NSDictionary* preferences = nil;
|
NSDictionary* preferences = nil;
|
||||||
|
@ -735,11 +769,8 @@ bail:
|
||||||
//alloc title
|
//alloc title
|
||||||
title = [NSMutableString string];
|
title = [NSMutableString string];
|
||||||
|
|
||||||
//alloc details
|
|
||||||
details = [NSMutableString string];
|
|
||||||
|
|
||||||
//alloc log msg
|
//alloc log msg
|
||||||
logMsg = [NSMutableString string];
|
sysLogMsg = [NSMutableString string];
|
||||||
|
|
||||||
//always (manually) load preferences
|
//always (manually) load preferences
|
||||||
preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]];
|
preferences = [NSDictionary dictionaryWithContentsOfFile:[APP_PREFERENCES stringByExpandingTildeInPath]];
|
||||||
|
@ -816,14 +847,14 @@ bail:
|
||||||
if(YES == [preferences[PREF_LOG_ACTIVITY] boolValue])
|
if(YES == [preferences[PREF_LOG_ACTIVITY] boolValue])
|
||||||
{
|
{
|
||||||
//init msg
|
//init msg
|
||||||
[logMsg appendString:@"OVERSIGHT: "];
|
[sysLogMsg appendString:@"OVERSIGHT: "];
|
||||||
|
|
||||||
//no process?
|
//no process?
|
||||||
// ->just add title / details
|
// ->just add title / details
|
||||||
if(nil == processName)
|
if(nil == processName)
|
||||||
{
|
{
|
||||||
//add
|
//add
|
||||||
[logMsg appendFormat:@"%@ (%@)", title, details];
|
[sysLogMsg appendFormat:@"%@ (%@)", title, details];
|
||||||
}
|
}
|
||||||
|
|
||||||
//process
|
//process
|
||||||
|
@ -831,11 +862,11 @@ bail:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//add
|
//add
|
||||||
[logMsg appendFormat:@"%@ (process: %@, %@)", title, details, getProcessPath([event[EVENT_PROCESS_ID] intValue])];
|
[sysLogMsg appendFormat:@"%@ (process: %@, %@)", title, details, processName];
|
||||||
}
|
}
|
||||||
|
|
||||||
//write it out to syslog
|
//write it out to syslog
|
||||||
syslog(LOG_ERR, "%s\n", logMsg.UTF8String);
|
syslog(LOG_ERR, "%s\n", sysLogMsg.UTF8String);
|
||||||
}
|
}
|
||||||
|
|
||||||
//set title
|
//set title
|
||||||
|
@ -850,6 +881,22 @@ bail:
|
||||||
//deliver notification
|
//deliver notification
|
||||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification: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
|
||||||
bail:
|
bail:
|
||||||
|
|
||||||
|
@ -941,7 +988,7 @@ bail:
|
||||||
dispatch_semaphore_t waitSema = nil;
|
dispatch_semaphore_t waitSema = nil;
|
||||||
|
|
||||||
//dbg msg
|
//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
|
//alloc XPC connection
|
||||||
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
|
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
|
||||||
|
@ -963,14 +1010,14 @@ bail:
|
||||||
waitSema = dispatch_semaphore_create(0);
|
waitSema = dispatch_semaphore_create(0);
|
||||||
|
|
||||||
//dbg msg
|
//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
|
//invoke XPC service to get (new) video procs
|
||||||
// ->will generate user notifications for any new processes
|
// ->will generate user notifications for any new processes
|
||||||
[[xpcConnection remoteObjectProxy] getVideoProcs:^(NSMutableArray* videoProcesses)
|
[[xpcConnection remoteObjectProxy] getVideoProcs:^(NSMutableArray* videoProcesses)
|
||||||
{
|
{
|
||||||
//dbg msg
|
//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
|
//generate a notification for each process
|
||||||
// ->double check video is still active though...
|
// ->double check video is still active though...
|
||||||
|
@ -1011,7 +1058,7 @@ bail:
|
||||||
xpcConnection = nil;
|
xpcConnection = nil;
|
||||||
|
|
||||||
//dbg msg
|
//dbg msg
|
||||||
logMsg(LOG_DEBUG, @"exiting monitor thread");
|
logMsg(LOG_DEBUG, @"[MONITOR THREAD] exiting polling/monitor thread since camera is off");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
|
|
||||||
@synthesize avMonitor;
|
@synthesize avMonitor;
|
||||||
@synthesize infoWindowController;
|
@synthesize infoWindowController;
|
||||||
@synthesize statusBarMenuController;
|
@synthesize statusBarMenuController;
|
||||||
|
|
|
@ -38,5 +38,31 @@
|
||||||
//about window controller
|
//about window controller
|
||||||
@property(nonatomic, retain)AboutWindowController* aboutWindowController;
|
@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
|
@end
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,9 @@
|
||||||
wasHandled = YES;
|
wasHandled = YES;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
//default
|
||||||
|
// ->do nothing
|
||||||
default:
|
default:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -265,5 +267,50 @@ bail:
|
||||||
return;
|
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
|
@end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16A323" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
|
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
|
||||||
<view key="contentView" id="EiT-Mj-1SZ">
|
<view key="contentView" id="EiT-Mj-1SZ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
|
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
@ -100,6 +100,16 @@
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Mzd-co-fJo">
|
||||||
|
<rect key="frame" x="389" y="13" width="77" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="Start!" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="zde-cc-enh">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" size="13" name="Menlo-Regular"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="startLoginItem:" target="Voe-Tx-rLC" id="8MX-Nf-yMA"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
</subviews>
|
</subviews>
|
||||||
</view>
|
</view>
|
||||||
</window>
|
</window>
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "main.h"
|
#import "main.h"
|
||||||
|
#import "Consts.h"
|
||||||
|
#import "Logging.h"
|
||||||
|
#import "Utilities.h"
|
||||||
#import "Enumerator.h"
|
#import "Enumerator.h"
|
||||||
#import "../Shared/Logging.h"
|
|
||||||
#import "../Shared/Utilities.h"
|
|
||||||
|
|
||||||
#import <libproc.h>
|
#import <libproc.h>
|
||||||
#import <sys/sysctl.h>
|
#import <sys/sysctl.h>
|
||||||
|
@ -39,6 +40,7 @@ static NSArray* ignoredProcs = nil;
|
||||||
@"/usr/sbin/notifyd",
|
@"/usr/sbin/notifyd",
|
||||||
@"/usr/sbin/syslogd",
|
@"/usr/sbin/syslogd",
|
||||||
@"/usr/sbin/cfprefsd",
|
@"/usr/sbin/cfprefsd",
|
||||||
|
@"/usr/libexec/avconferenced",
|
||||||
@"/usr/libexec/opendirectoryd",
|
@"/usr/libexec/opendirectoryd",
|
||||||
@"/usr/libexec/UserEventAgent",
|
@"/usr/libexec/UserEventAgent",
|
||||||
@"/System/Library/CoreServices/launchservicesd",
|
@"/System/Library/CoreServices/launchservicesd",
|
||||||
|
@ -119,7 +121,7 @@ static NSArray* ignoredProcs = nil;
|
||||||
//get name
|
//get name
|
||||||
processPath = getProcessPath(pids[i]);
|
processPath = getProcessPath(pids[i]);
|
||||||
if( (nil == processPath) ||
|
if( (nil == processPath) ||
|
||||||
(0 == processPath.length) )
|
(0 == processPath.length) )
|
||||||
{
|
{
|
||||||
//skip
|
//skip
|
||||||
continue;
|
continue;
|
||||||
|
@ -180,7 +182,7 @@ bail:
|
||||||
self.machSenders = [self enumMachSenders:[self findCameraAssistant]];
|
self.machSenders = [self enumMachSenders:[self findCameraAssistant]];
|
||||||
|
|
||||||
//dbg msg
|
//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];
|
currentSenders = [self enumMachSenders:cameraAssistant];
|
||||||
|
|
||||||
//dbg msg
|
//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
|
//remove any known/existing senders
|
||||||
for(NSNumber* processID in currentSenders.allKeys)
|
for(NSNumber* processID in currentSenders.allKeys)
|
||||||
|
@ -253,12 +255,13 @@ bail:
|
||||||
}
|
}
|
||||||
|
|
||||||
//dbg msg
|
//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
|
//update
|
||||||
self.machSenders = currentSenders;
|
self.machSenders = currentSenders;
|
||||||
|
|
||||||
//invoke 'sample' to confirm that candidates are using CMIO/video inputs
|
//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];
|
videoProcs = [self sampleCandidates:candidateVideoProcs];
|
||||||
|
|
||||||
}//sync
|
}//sync
|
||||||
|
@ -285,6 +288,9 @@ bail:
|
||||||
//process id
|
//process id
|
||||||
NSNumber* processID = nil;
|
NSNumber* processID = nil;
|
||||||
|
|
||||||
|
//process path
|
||||||
|
NSString* processPath = nil;
|
||||||
|
|
||||||
//alloc
|
//alloc
|
||||||
senders = [NSMutableDictionary dictionary];
|
senders = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
@ -340,8 +346,18 @@ bail:
|
||||||
continue;
|
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)
|
//ignore apple daemons (that send mach messages, etc)
|
||||||
if(YES == [ignoredProcs containsObject:getProcessPath(processID.intValue)])
|
if(YES == [ignoredProcs containsObject:processPath])
|
||||||
{
|
{
|
||||||
//skip
|
//skip
|
||||||
continue;
|
continue;
|
||||||
|
@ -366,12 +382,48 @@ bail:
|
||||||
//results from 'sample' cmd
|
//results from 'sample' cmd
|
||||||
NSString* results = nil;
|
NSString* results = nil;
|
||||||
|
|
||||||
|
//process path
|
||||||
|
NSString* processPath = nil;
|
||||||
|
|
||||||
//alloc
|
//alloc
|
||||||
videoProcs = [NSMutableArray array];
|
videoProcs = [NSMutableArray array];
|
||||||
|
|
||||||
//invoke 'sample' on each
|
//invoke 'sample' on each
|
||||||
|
// ->skips FaceTime.app though on macOS Sierra
|
||||||
for(NSNumber* processID in currentSenders)
|
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
|
//exec 'sample' to get threads/dylibs
|
||||||
// ->uses 1.0 seconds for sampling time
|
// ->uses 1.0 seconds for sampling time
|
||||||
results = [[NSString alloc] initWithData:execTask(SAMPLE, @[processID.stringValue, @"1"]) encoding:NSUTF8StringEncoding];
|
results = [[NSString alloc] initWithData:execTask(SAMPLE, @[processID.stringValue, @"1"]) encoding:NSUTF8StringEncoding];
|
||||||
|
@ -384,7 +436,7 @@ bail:
|
||||||
|
|
||||||
//sampling a process creates a temp file
|
//sampling a process creates a temp file
|
||||||
//->delete it!
|
//->delete it!
|
||||||
[self deleteSampleFile:getProcessPath(processID.intValue)];
|
[self deleteSampleFile:processPath];
|
||||||
|
|
||||||
//for now, just check for 'CMIOGraph::DoWork'
|
//for now, just check for 'CMIOGraph::DoWork'
|
||||||
// ->TODO: could look for dylibs, other calls, etc
|
// ->TODO: could look for dylibs, other calls, etc
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
// Copyright (c) 2016 Objective-See. All rights reserved.
|
// Copyright (c) 2016 Objective-See. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#import "XPCProtocol.h"
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "../Shared/XPCProtocol.h"
|
|
||||||
|
|
||||||
/* DEFINES */
|
/* DEFINES */
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import "Logging.h"
|
#import "Logging.h"
|
||||||
|
#import "Utilities.h"
|
||||||
#import "Enumerator.h"
|
#import "Enumerator.h"
|
||||||
#import "OverSightXPC.h"
|
#import "OverSightXPC.h"
|
||||||
#import "../Shared/Utilities.h"
|
|
||||||
|
|
||||||
@implementation OverSightXPC
|
@implementation OverSightXPC
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,9 @@
|
||||||
#import <bsm/libbsm.h>
|
#import <bsm/libbsm.h>
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
#import "../Shared/Exception.h"
|
#import "Logging.h"
|
||||||
#import "../Shared/XPCProtocol.h"
|
#import "Exception.h"
|
||||||
#import "../Shared/Logging.h"
|
#import "XPCProtocol.h"
|
||||||
|
|
||||||
#import "OverSightXPC.h"
|
#import "OverSightXPC.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,10 +28,6 @@
|
||||||
//TODO: test final/with page
|
//TODO: test final/with page
|
||||||
#define PRODUCT_VERSION_URL @"https://objective-see.com/products/versions/oversight.json"
|
#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
|
//OS version x
|
||||||
#define OS_MAJOR_VERSION_X 10
|
#define OS_MAJOR_VERSION_X 10
|
||||||
|
|
||||||
|
@ -88,4 +84,7 @@
|
||||||
//path to pkill
|
//path to pkill
|
||||||
#define PKILL @"/usr/bin/pkill"
|
#define PKILL @"/usr/bin/pkill"
|
||||||
|
|
||||||
|
//path to facetime
|
||||||
|
#define FACE_TIME @"/Applications/FaceTime.app/Contents/MacOS/FaceTime"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
|
|
||||||
/* FUNCTIONS */
|
/* FUNCTIONS */
|
||||||
|
|
||||||
|
//get OS version
|
||||||
|
NSDictionary* getOSVersion();
|
||||||
|
|
||||||
//get app's version
|
//get app's version
|
||||||
// ->extracted from Info.plist
|
// ->extracted from Info.plist
|
||||||
NSString* getAppVersion();
|
NSString* getAppVersion();
|
||||||
|
@ -52,6 +55,10 @@ NSString* getProcessPath(pid_t pid);
|
||||||
// ->get the name of the process
|
// ->get the name of the process
|
||||||
NSString* getProcessName(pid_t pid);
|
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
|
//wait until a window is non nil
|
||||||
// ->then make it modal
|
// ->then make it modal
|
||||||
void makeModal(NSWindowController* windowController);
|
void makeModal(NSWindowController* windowController);
|
||||||
|
|
|
@ -20,6 +20,55 @@
|
||||||
#import <CommonCrypto/CommonDigest.h>
|
#import <CommonCrypto/CommonDigest.h>
|
||||||
#import <SystemConfiguration/SystemConfiguration.h>
|
#import <SystemConfiguration/SystemConfiguration.h>
|
||||||
|
|
||||||
|
//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
|
//get app's version
|
||||||
// ->extracted from Info.plist
|
// ->extracted from Info.plist
|
||||||
NSString* getAppVersion()
|
NSString* getAppVersion()
|
||||||
|
@ -178,8 +227,6 @@ BOOL setFilePermissions(NSString* file, int permissions, BOOL recursive)
|
||||||
//set file permissions on each
|
//set file permissions on each
|
||||||
for(NSURL* currentFile in enumerator)
|
for(NSURL* currentFile in enumerator)
|
||||||
{
|
{
|
||||||
NSLog(@"current file: %@", currentFile.path);
|
|
||||||
|
|
||||||
//set permissions
|
//set permissions
|
||||||
if(YES != [[NSFileManager defaultManager] setAttributes:filePermissions ofItemAtPath:currentFile.path error:&error])
|
if(YES != [[NSFileManager defaultManager] setAttributes:filePermissions ofItemAtPath:currentFile.path error:&error])
|
||||||
{
|
{
|
||||||
|
@ -459,6 +506,81 @@ bail:
|
||||||
return processName;
|
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
|
//determine if there is a new version
|
||||||
// -1, YES or NO
|
// -1, YES or NO
|
||||||
NSInteger isNewVersion(NSMutableString* versionString)
|
NSInteger isNewVersion(NSMutableString* versionString)
|
||||||
|
@ -575,3 +697,4 @@ void makeModal(NSWindowController* windowController)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue