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:
Patrick Wardle 2016-09-24 17:08:38 -10:00
parent d9ae15b3bc
commit 2eddae193d
16 changed files with 381 additions and 81 deletions

View File

@ -135,4 +135,6 @@ bail:
return;
}
- (IBAction)startLoginItem:(id)sender {
}
@end

View File

@ -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;

View File

@ -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]];

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16A323" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
</dependencies>
@ -20,7 +20,7 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="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="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">
<rect key="frame" x="0.0" y="0.0" width="460" height="176"/>
<autoresizingMask key="autoresizingMask"/>
@ -43,7 +43,7 @@
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon" id="bCU-0f-ff8"/>
</imageView>
<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">
<font key="font" size="13" name="Menlo-Regular"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>

View File

@ -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;
}

View File

@ -19,7 +19,6 @@
@implementation AppDelegate
@synthesize avMonitor;
@synthesize infoWindowController;
@synthesize statusBarMenuController;

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16A323" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
@ -28,7 +28,7 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="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="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">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
@ -100,6 +100,16 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</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>
</view>
</window>

View File

@ -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 <libproc.h>
#import <sys/sysctl.h>
@ -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

View File

@ -6,8 +6,8 @@
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "XPCProtocol.h"
#import <Foundation/Foundation.h>
#import "../Shared/XPCProtocol.h"
/* DEFINES */

View File

@ -7,9 +7,10 @@
//
#import "Logging.h"
#import "Utilities.h"
#import "Enumerator.h"
#import "OverSightXPC.h"
#import "../Shared/Utilities.h"
@implementation OverSightXPC

View File

@ -12,10 +12,9 @@
#import <bsm/libbsm.h>
#import <Foundation/Foundation.h>
#import "../Shared/Exception.h"
#import "../Shared/XPCProtocol.h"
#import "../Shared/Logging.h"
#import "Logging.h"
#import "Exception.h"
#import "XPCProtocol.h"
#import "OverSightXPC.h"

View File

@ -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

View File

@ -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);

View File

@ -20,6 +20,55 @@
#import <CommonCrypto/CommonDigest.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
// ->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)