關(guān)于MAC OS X##
去年十月份從iOS開發(fā)轉(zhuǎn)到OS X開發(fā),OS X主要由五大API來開發(fā),分別是`Cocoa`、`Carbon`、`POSIX`、`X11`和`Java`。目前我用的是`Cocoa`來開發(fā),OS X開發(fā)中考慮的問題更多一些,網(wǎng)上的資料更少一些,遇到問題后最好的解決辦法就是查看[官方文檔](https://developer.apple.com/search/?q=mac%20os%20x)。
什么時(shí)候需要授權(quán)?
在OS X開發(fā)中,我們會(huì)經(jīng)常遇到權(quán)限的問題,比如你安裝APP的時(shí)候,你如你需要?jiǎng)h除一些配置文件的時(shí)候都需要權(quán)限,不然就無法操作,我們的項(xiàng)目中需要做一個(gè)卸載工具和退域工具,里面涉及到刪除配置文件以及退出進(jìn)程等一些需要權(quán)限的操作。
具體內(nèi)容
刪除配置文件的時(shí)候,通常情況下通過`NSTask`這個(gè)類打開終端,輸入命令進(jìn)行操作的,如:
[NSTask launchedTaskWithLaunchPath:@"/usr/bin/sudo" arguments: [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.tencent.MacScmClient.Daemon.plist",nil]];
這行代碼就是用NSTask來調(diào)用終端來進(jìn)行刪除文件的,大概意思就是在終端輸入sudo,然后執(zhí)行launchctl和unload的命令,但是呢,怎樣寫IE肯定是不行的,為什么呢,因?yàn)槟銢]有權(quán)限這么做,有興趣的同學(xué)可以在終端嘗試一下,輸入這個(gè)命令是需要輸入密碼的,所以怎么獲取權(quán)限呢?
之前我們的工程中有獲取授權(quán)的代碼,這個(gè)下文再說,當(dāng)時(shí)同事寫了一段代碼使用AppleScript來獲取權(quán)限的,我再網(wǎng)上找了下,也找到了原文,具體的代碼如下:
Boolean runProcessAsAdministrator( NSString *scriptPath, NSArray *arguments,BOOL isAdmin, NSString **output,NSString **errorDescription)
{
NSString * allArgs = [arguments componentsJoinedByString:@" "];
NSString *isAdminPre = @"";
if (isAdmin) {
isAdminPre = @"with administrator privileges";
}
NSString * fullScript = [NSString stringWithFormat:@"%@ %@", scriptPath, allArgs];
NSDictionary *errorInfo = [NSDictionary new];
NSString *script = [NSString stringWithFormat:@"do shell script \"%@\" %@", fullScript, isAdminPre];
NSLog(@"script = %@",script);
NSAppleScript *appleScript = [[NSAppleScript new] initWithSource:script];
NSAppleEventDescriptor * eventResult = [appleScript executeAndReturnError:&errorInfo];
// Check errorInfo/var/tmp
if (! eventResult)
{
// Describe common errors
*errorDescription = nil;
if ([errorInfo valueForKey:NSAppleScriptErrorNumber])
{
NSNumber * errorNumber = (NSNumber *)[errorInfo valueForKey:NSAppleScriptErrorNumber];
if ([errorNumber intValue] == -128)
*errorDescription = @"The administrator password is required to do this.";
}
// Set error message from provided message
if (*errorDescription == nil)
{
if ([errorInfo valueForKey:NSAppleScriptErrorMessage])
*errorDescription = (NSString *)[errorInfo valueForKey:NSAppleScriptErrorMessage];
}
return NO;
}
else
{
// Set output to the AppleScript's output
*output = [eventResult stringValue];
return YES;
}
}
這段代碼就是用AppleScript來獲取權(quán)限的,使用方法如下:
NSString *output = @"";
NSString *error= @"";
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);
但是這么寫會(huì)有一個(gè)問題,就是我是獲得權(quán)限了,但是卸載的時(shí)候往往不可能只刪除一個(gè)配置文件,而是要?jiǎng)h除很多個(gè)配置文件,起初我是這么寫的:
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);
NSLog(@"output = %@, error = %@", output, error);
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.******.******.Daemon.plist",nil], true, &output, &error);
NSLog(@"output = %@, error = %@", out
我就很開心的運(yùn)行,但是問題來了,什么問題呢?就是你刪除幾個(gè)配置文件就需要輸入幾次用戶名和密碼,這樣的話用戶體驗(yàn)相當(dāng)?shù)牟缓?,理想狀態(tài)是什么呢?輸入一次用戶名和密碼之后,獲取的權(quán)限以后其他的刪除配置文件的操作就不需要再次獲取權(quán)限了,所以我就看了下前輩們是怎么寫的,大概的代碼如下:
int main(int argc, char * argv[]) {
@autoreleasepool {
if (argc == 2) {
NSLog(@"AuthHelperTool executing self-repair");
OSStatus myStatus;
AuthorizationFlags myFlags = kAuthorizationFlagDefaults;
AuthorizationRef myAuthorizationRef;
myStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, myFlags, &myAuthorizationRef);
if (myStatus != errAuthorizationSuccess)
return myStatus;
AuthorizationItem myItems = {kAuthorizationRightExecute, 0, NULL, 0};
AuthorizationRights myRights = {1, &myItems};
myFlags = kAuthorizationFlagDefaults |
kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagPreAuthorize |
kAuthorizationFlagExtendRights;
myStatus = AuthorizationCopyRights (myAuthorizationRef, &myRights, NULL, myFlags, NULL );
if (myStatus != errAuthorizationSuccess)
return myStatus;
char *myToolPath = argv[1];
char *myArguments[] = {argv[1], "--fix", NULL};
FILE *myCommunicationsPipe = NULL;
myFlags = kAuthorizationFlagDefaults;
//這句是獲取管理員權(quán)限的代碼
myStatus = AuthorizationExecuteWithPrivileges(myAuthorizationRef, myToolPath, myFlags, myArguments, &myCommunicationsPipe);
return 0;
}
else if (argc == 3){
NSString *output = @"";
NSString *error= @"";
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.******.******.Daemon.plist",nil], true, &output, &error);
}
這段代碼是個(gè)可執(zhí)行文件,然后在ViewController中調(diào)用的這個(gè)可執(zhí)行文件。我們看到這個(gè)獲取權(quán)限的方法是使用Authorization的類來獲取權(quán)限的,這個(gè)也是官方文檔中的獲取權(quán)限的方法,具體的可以參考這里
,繼續(xù)說上面的代碼,前文說了這個(gè)是個(gè)可執(zhí)行文件,是在ViewController中調(diào)用的,代碼如下:
NSString *helperToolPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:@"/ExitDomain"] ;
NSLog(@"path = %@",helperToolPath);
NSArray *args = [NSArray arrayWithObjects:helperToolPath, nil];
[NSTask launchedTaskWithLaunchPath:helperToolPath arguments:args];
從這兒看到,我們是用NSTask這個(gè)類來打開終端來執(zhí)行這個(gè)可執(zhí)行文件的,參數(shù)為helperToolPath也就是這個(gè)可執(zhí)行文件的路徑,所以到了可執(zhí)行文件的mian函數(shù)中,argc就是2,然后來執(zhí)行下面的獲取權(quán)限的代碼的,下面獲取權(quán)限的代碼有個(gè)需要注意的地方:
char *myToolPath = argv[1];
char *myArguments[] = {argv[1], "--fix", NULL};
FILE *myCommunicationsPipe = NULL;
myFlags = kAuthorizationFlagDefaults;
//這句是獲取管理員權(quán)限的代碼
myStatus = AuthorizationExecuteWithPrivileges(myAuthorizationRef, myToolPath, myFlags, myArguments, &myCommunicationsPipe);
這幾行代碼需要注意,首先myToolPath這個(gè)參數(shù)就是我們從ViewController中配置的那個(gè)參數(shù),也就是可執(zhí)行文件的路徑,其次myArguments里面多了個(gè)--fix,這個(gè)不是命令,只是為了使argc的個(gè)數(shù)變?yōu)槿?,?/p>
myStatus = AuthorizationExecuteWithPrivileges(myAuthorizationRef, myToolPath, myFlags, myArguments, &myCommunicationsPipe);
這行代碼就是獲取管理員權(quán)限的代碼,從官方文檔來看在10.1和10.7中是不能使用的,具體的可以看官方文檔,如果想測試自己的系統(tǒng)能不能用,請參考github上的這個(gè)第三方庫中用的測試方法,然后這行代碼還有個(gè)作用,就是它又調(diào)用了一次這個(gè)可執(zhí)行文件,方法中有個(gè)參數(shù)是myToolPath,這個(gè)就是可執(zhí)行文件的路徑,所以esle if中的代碼才會(huì)執(zhí)行,按照這種方法寫完后就只需要輸入一次用戶名和密碼,用戶體驗(yàn)明顯好轉(zhuǎn)。
總結(jié)##
本文講了兩種獲取權(quán)限的方法,一種是使用`AppleScript`來獲取權(quán)限,這樣寫是能獲取到權(quán)限,但是如多刪除多個(gè)文件的話,就需要輸入多次用戶名和密碼;另一種是用`Authorization`這個(gè)類來獲取權(quán)限,先獲取到權(quán)限后,然后再執(zhí)行刪除配置文件的操作就可以了,需要注意的地方就是,我再使用第二種方法的時(shí)候`esle if`中調(diào)用的還是`AppleScript`刪除文件的方法,沒有用`NSTask`這個(gè)類,這個(gè)說明一下,這兒完全可以使用`NSTask`這個(gè)類做這些事情,也就是下面這段代碼:
runProcessAsAdministrator(@"", [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchAgents/com.******.******.plist",nil],false, &output, &error);
完全可以用這段替換掉:
[NSTask launchedTaskWithLaunchPath:@"/usr/bin/sudo" arguments: [NSArray arrayWithObjects:@"launchctl", @"unload", @"/Library/LaunchDaemons/com.tencent.MacScmClient.Daemon.plist",nil]];
這個(gè)只是我項(xiàng)目中遇到的一些關(guān)于權(quán)限的問題以及解決辦法,還有很多不足之處,希望各路大神多多指正,我也會(huì)再繼續(xù)深入學(xué)習(xí),及時(shí)修改這篇文章。
參考資料
[https://developer.apple.com/search/?q=Authorization&type=Guides]
[http://stackoverflow.com/questions/29796363/how-to-add-root-privileges-to-my-osx-application]
[http://codego.net/213006/]
[http://www.tanhao.me/pieces/1279.html/]
[https://github.com/sveinbjornt/STPrivilegedTask]