Cocoa中的AuthorizationExecuteWithPriveleges非常奇怪

时间:2011-11-11 13:31:29

标签: objective-c macos cocoa bash terminal

我使用AuthorizationExecuteWithPriveleges使用管理员权限从我的应用程序执行bash命令。我发现了非常奇怪的问题。我在这里使用

    FILE *pipe=nil;
    OSStatus err;
    AuthorizationRef authorizationRef;
    char *command= "/bin/chmod";


    char *args[] = {"644","folderPath", nil};

   if(err!=0)
    {
                                err = AuthorizationCreate(nil,
                                       kAuthorizationEmptyEnvironment,
                                       kAuthorizationFlagDefaults,
                                       &authorizationRef);
    }
    NSLog(@"test");
    err = AuthorizationExecuteWithPrivileges(authorizationRef,
                                             command,
                                             kAuthorizationFlagDefaults,
                                             args,
                                             &pipe);  

调用此功能约40次后,它的响应速度非常慢。它会死后,冻结应用程序,我不知道发生了什么。它没有显示日志"测试",并没有做任何事情,在打了大约40次之后。 使用什么Bash命令或什么参数并不重要。它仍然做同样的事情。这有什么问题?我之所以使用它,是因为我的应用程序也需要在10.5上运行。

如果有人有想法,请问我能做些什么。对此,我真的非常感激。我需要尽快。感谢

4 个答案:

答案 0 :(得分:3)

看了一下这个,并编写了以下示例,没有保修,但是对于我来说,对于我的数千次AuthorizationExecuteWithPrivileges调用没有问题:

void DoOtherStuff(AuthorizationRef auth, char* path);

void DoStuff(char* path)
{
    AuthorizationItem foo;
    foo.name = kAuthorizationRightExecute;
    foo.value = NULL;
    foo.valueLength = 0;
    foo.flags = 0;

    AuthorizationRights rights;
    rights.count = 1;
    rights.items = &foo;

    AuthorizationRef authorizationRef;
    OSStatus err = errAuthorizationSuccess;

    if (errAuthorizationSuccess != (err = AuthorizationCreate(NULL,  kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef)))    
    {
        NSLog(@"Error on AuthorizationCreate: %lu", (long)err);
        return;
    }

    for (NSUInteger i = 0; i < 5000; i++)
    {
        NSLog(@"Doing run: %lu", (long)i+1);
        DoOtherStuff(authorizationRef, "/tmp/foo");
    }

    if (errAuthorizationSuccess != (err = AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults)))
    {
        NSLog(@"Error on AuthorizationFree: %lu", (long)err);
        return;
    }
}

void DoOtherStuff(AuthorizationRef authorizationRef, char* path)
{    
    OSStatus err = errAuthorizationSuccess;
    FILE *pipe = NULL;
    @try
    {
        char *args[] = {"644", path, NULL};
        if (errAuthorizationSuccess != (err = AuthorizationExecuteWithPrivileges(authorizationRef,
                                                 "/bin/chmod", kAuthorizationFlagDefaults, args, &pipe)))
        {
            NSLog(@"Error on AuthorizationExecuteWithPrivileges: %lu", (long)err);
            return;
        }

        int stat;
        wait(&stat);

        NSLog(@"Success! Child Process Died!");
    }
    @finally 
    {        
        if (pipe)
            fclose(pipe);
    }
}

克里斯苏特所说的已经死了。调用AuthorizationExecuteWithPrivileges时会发生的事情是fork()是你的进程,然后exec()从子进程执行请求的进程(在本例中为chmod)。在有人调用wait()之前,不会获得子进程,但这很难,因为我们没有从AuthorizationExecuteWithPrivileges获取子进程的PID(它将由fork()返回)。正如他所说,如果你确定没有其他线程同时产生进程(即你的线程是唯一一个创建子进程的线程),那么你可以只调用wait()的非PID特定版本我在这个例子中做。

如果你不调用wait(),那么你会积累这些等待收获的僵尸子进程。最终操作系统说“不再”。

我感觉有点不好发布这个,因为它只是对Chris Suter所说的翻新;我赞成他的回答。

为了完整性,这里是该示例的重写版本,通过忽略SIGCHLD而不是调用wait来实现目标。它也没有保修,但对我有用。

void DoOtherStuff(AuthorizationRef auth, char* path);

void DoStuff(char* path)
{
    AuthorizationItem foo;
    foo.name = kAuthorizationRightExecute;
    foo.value = NULL;
    foo.valueLength = 0;
    foo.flags = 0;

    AuthorizationRights rights;
    rights.count = 1;
    rights.items = &foo;

    AuthorizationRef authorizationRef;
    OSStatus err = errAuthorizationSuccess;

    struct sigaction oldAction;
    struct sigaction newAction;

    newAction.__sigaction_u.__sa_handler = SIG_IGN;
    newAction.sa_mask = 0;
    newAction.sa_flags = 0;

    if(0 != sigaction(SIGCHLD, &newAction, &oldAction))
    {
        NSLog(@"Couldn't ignore SIGCHLD");
        return;
    }

    @try
    {
        if (errAuthorizationSuccess != (err = AuthorizationCreate(NULL,  kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef)))    
        {
            NSLog(@"Error on AuthorizationCreate: %lu", (long)err);
            return;
        }

        for (NSUInteger i = 0; i < 1000; i++)
        {
            NSLog(@"Doing run: %lu", (long)i+1);
            DoOtherStuff(authorizationRef, "/tmp/foo");
        }

        if (errAuthorizationSuccess != (err = AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults)))
        {
            NSLog(@"Error on AuthorizationFree: %lu", (long)err);
            return;
        }
    }
    @finally 
    {
        const struct sigaction cOldAction = oldAction;
        if(0 != sigaction(SIGCHLD, &cOldAction, NULL))
        {
            NSLog(@"Couldn't restore the handler for SIGCHLD");
            return;
        }

    }
}

void DoOtherStuff(AuthorizationRef authorizationRef, char* path)
{ 
    OSStatus err = errAuthorizationSuccess;
    FILE *pipe = NULL;
    @try
    {
        char *args[] = {"644", path, NULL};
        if (errAuthorizationSuccess != (err = AuthorizationExecuteWithPrivileges(authorizationRef,
                                                 "/bin/chmod", kAuthorizationFlagDefaults, args, &pipe)))
        {
            NSLog(@"Error on AuthorizationExecuteWithPrivileges: %lu", (long)err);
            return;
        }

        NSLog(@"Success!");
    }
    @finally 
    {        
        if (pipe)
            fclose(pipe);
    }
}

答案 1 :(得分:2)

你要做的不是一个好主意。

我猜你的代码中还有一个bug,也许是在监视管道时。我们需要看到你的其余代码。

如果您采用这种方法,则需要注意并确保清理僵尸进程,这在使用AuthorizationExecuteWithPrivileges时可能会很尴尬,因为您没有获得子进程ID。您可能需要忽略SIGCHLD,或者如果您可以确定没有其他线程同时处理进程,您可以只调用wait

您还必须确保清理管道,否则您将耗尽文件描述符。

系统对于泄漏文件描述符或进程的能力远不如泄漏内存那么宽容。

解决问题的正确方法可能是编写一个帮助工具,然后与帮助工具通信,要求它代表您执行特权操作。这样你只需运行一次辅助工具。您应该能够在Apple的文档中阅读更多相关信息。

答案 2 :(得分:1)

您应该初始化err(由于第一个IF语句),因为它不能保证为0.但是,它可能是,因此您正在跳过AuthorizationCreate,因此不会创建授权会话。

基本上你将authorizationRef未初始化传递给AuthorizationExecuteWithPrivileges,这可能是个问题。

再加上其他人,我会把AuthorizationFree(authorizationRef,kAuthorizationFlagDefaults);最后,当您使用AuthorizationCreate释放内存时。

另外值得注意的是,从OS X v10.7开始,不推荐使用AuthorizationExecuteWithPrivileges,但我想你知道,因为你说你试图在10.5上运行

编辑:您可能希望在运行AuthorizationCreate

后检查错误的状态
if ( err != errAuthorizationSuccess ) {
  return;
}

...你也应该在AuthorizationExecuteWithPrivileges之后检查错误。

答案 3 :(得分:0)

我想我可能知道这里发生了什么:尝试正确处理管道(即不要传递NULL,并确保关闭它)。如果你没有给它一个STDIN管道,NSTask也会发生像这样奇怪的事情。 cocoadev.com上的This page解释说:

  

如果执行,NSTask将完全破坏Xcode的调试日志   任何与sh或bash(包括脚本)相关的东西。 printf,NSLog;   一旦任务启动,所有这些都将停止运作。甚至   右键单击调试器中的对象之类的东西会产生   什么都没有(直接GDB仍打印)。 ......我想通了   问题在于标准输入,所有事情。快速修复此问题   将标准输入设置为随机的,例如a   管道,并没有做任何事情。

这让我困扰了好几个小时(与NSTask合作而不是AS)。如果您所看到的奇怪行为与此无关,我会感到惊讶。确保你没有传递NULL,然后通过调用fclose等确保你正在清理AuthorizationExecuteWithPrivileges为你创建的文件句柄。

我不是百分百肯定,但“NSLog停止工作”症状引起了我的注意。