Очень странная проблема с AuthorizationExecuteWithPriveleges в Какао - PullRequest
2 голосов
/ 11 ноября 2011

Я использую 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-кратного вызова этой функции она начинает реагировать очень медленно.И после того, как он просто умрет и заморозит приложение, и я понятия не имею, что с этим происходит. Он не показывает журнал «test» и ничего не делает, после вызова около 40 раз. Неважно, какую команду Bash или какие аргументы вы используете.Это все еще делает то же самое. Что не так с этим?Причина, по которой я это использую, потому что мое приложение должно работать и на 10.5.

Пожалуйста, если у кого-то есть идеи, что я могу сделать.Я очень ценю это.Мне нужно как можно скорее.Спасибо

Ответы [ 4 ]

3 голосов
/ 20 ноября 2011

Посмотрел на это немного больше и подготовил следующий пример, представленный без гарантии, но который работает для меня для тысяч вызовов 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 (), но это сложно, потому что мы не получаем PID дочернего процесса из AuthorizationExecuteWithPrivileges (он был бы возвращен fork ()). По его словам, если вы уверены, что нет других потоков, порождающих процессы одновременно (т. Е. Ваш поток является единственным, создающим дочерние процессы), то вы можете просто вызвать не ожидающую PID версию wait (), как Я делаю в этом примере.

Если вы не вызываете wait (), то происходит то, что вы накапливаете эти дочерние процессы-зомби, которые все ждут, когда их соберут. В конце концов ОС говорит «больше нет».

Я чувствую себя немного плохо, публикуя это, поскольку это просто повторение того, что сказал Крис Сутер; Я проголосовал за его ответ.

Для полноты, вот переработанная версия этого примера, которая достигает цели, игнорируя 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);
    }
}
2 голосов
/ 17 ноября 2011

То, что вы пытаетесь сделать, не очень хорошая идея.

Я бы предположил, что у вас есть ошибка еще где-то в вашем коде, возможно, в мониторинге канала.Нам нужно увидеть остальную часть вашего кода.

Если вы продолжаете использовать этот подход, вам нужно будет позаботиться и убедиться, что вы очищаете процессы зомби, что может быть неудобно при использовании AuthorizationExecuteWithPrivileges, потому что вы этого не делаетеполучить идентификатор дочернего процесса.Вам нужно либо игнорировать SIGCHLD, либо, если вы можете быть уверены, что нет других потоков, которые одновременно работают с процессами, вы можете просто вызвать wait.

Вы также должны убедиться, что вы очистили канал, так как в противном случае у вас закончатся файловые дескрипторы.

Система гораздо менее прощающа, если вы пропускаете файловые дескрипторы или процессы, чем утечка памяти.

Правильный подход к вашей проблеме, вероятно, состоит в том, чтобы написать вспомогательный инструмент, а затем связаться с вашим вспомогательным инструментом и попросить его выполнить привилегированные операции от вашего имени.Таким образом, вы будете запускать свой вспомогательный инструмент только один раз.Вы должны быть в состоянии прочитать больше об этом в документации Apple.

1 голос
/ 20 ноября 2011

Вы должны инициализировать ошибку (из-за первого оператора IF), потому что он не гарантированно равен 0. Однако, вероятно, так и есть, поэтому вы пропускаете AuthorizationCreate, поэтому авторизованный сеанс не создается.

Обычно вы передаете AuthorizationRef неинициализированным AuthorizationExecuteWithPrivileges, что может быть проблемой.

Плюс, как и другие, я бы поставил AuthorizationFree (authorizationRef, kAuthorizationFlagDefaults); и в конце, когда вы используете AuthorizationCreate для освобождения памяти.

Также стоит отметить, что AuthorizationExecuteWithPrivileges устарела с OS X v10.7, но я думаю, вы знаете, что, поскольку вы сказали, что пытаетесь работать на 10.5

РЕДАКТИРОВАТЬ: Возможно, вы захотите проверить состояние ошибки также после запуска AuthorizationCreate

if ( err != errAuthorizationSuccess ) {
  return;
}

... вы также должны проверить ошибку после AuthorizationExecuteWithPrivileges.

0 голосов
/ 18 ноября 2011

Я думаю, что я мог бы знать, что здесь происходит: попробуйте правильно обработать трубу (то есть, не передавайте NULL, и убедитесь, что вы закрыли его). Странные вещи, подобные этому, случаются и с NSTask, если вы не можете дать ему канал STDIN. Эта страница на сайте cocoadev.com объясняет:

NSTask полностью прервет журнал отладки Xcode, если вы выполните НИЧЕГО связанного с sh или bash (включая скрипты). printf, NSLog; все перестанет функционировать, как только задача будет запущена. Четное такие вещи, как щелчок правой кнопкой мыши на объекте в отладчике приведет к ничего (прямой GDB все еще печатает все же). ... я понял, что проблема заключается в стандартном вводе всех вещей. Быстрое решение для этого было бы установить ваш стандартный ввод до чего-то случайного, как труба, и ничего с этим не делать.

Это поставило меня в тупик на многие часы (с NSTask, а не с AS). Я был бы удивлен, если странное поведение, которое вы видите, не связано. Убедитесь, что вы не передаете NULL, а затем убедитесь, что вы очищаете дескриптор файла, который создает для вас AuthorizationExecuteWithPrivileges, вызывая fclose и т. Д.

Я не уверен на 100%, но на глаза мне попался симптом «NSLog прекратил работать».

...