Технические вопросы и ответы QA1133

Определение состояния входа в систему пользователя консоли

Q: Как я говорю, зарегистрирован ли в настоящее время пользователь консоли?

A: Как я говорю, зарегистрирован ли в настоящее время пользователь консоли?

Это действительно зависит от того, чем Вы подразумеваете, «в настоящее время входил в систему». С введением быстрого переключения между пользователями в Mac OS X 10.3, в многочисленных пользователей можно войти система одновременно. Так, необходимо разделить вопрос в два:

  1. Как я получаю список сеансов входа в систему GUI?

  2. Какой сеанс входа в систему GUI, если таковые имеются, в настоящее время использует консоль?

Это ясные вопросы с ясными ответами (см. ниже). Однако вероятно, что, если Вы задаете эти вопросы, необходимо заново продумать архитектуру.

Один из фундаментальных принципов разработки Mac OS X - то, что высокоуровневые службы могут зависеть от низкоуровневых служб, но не наоборот. Например, для приложения хорошо зависеть от ядра, но это - проблема, если ядро зависит от приложения.

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

Этот вопрос обсужден в большой глубине в Техническом примечании TN2083, 'Демоны и Агенты' и, в частности, в разделе «Design Considerations» этого technote. Так, перед чтением остальной части этого документа я строго рекомендую считать, что technote и думают о том, как можно выровнять архитектуру с полной архитектурой Mac OS X.

Из-за этих проблем, подпрограмм как SCDynamicStoreCopyConsoleUser существуйте только в целях совместимости. Вероятно, что они будут формально осуждаться в будущей версии Mac OS X.

Если Вы принимаете решение проигнорировать все предупреждения выше, вот ответы на Ваши вопросы:

  1. Как я получаю список сеансов входа в систему GUI? — В Mac OS X 10.5 и позже можно использовать utmpx API для получения этой информации. Для примера этого посмотрите Пример кода 'UTXplorer'.

    До Mac OS X 10.5 не было никакого поддерживаемого способа получить эту информацию непосредственно. Техническое примечание TN2083, 'Демоны и Агенты' предлагают косвенный способ сделать это, базировало центрального демона с рядом сотрудничающих агентов. Этот проект будет работать над всеми версиями Mac OS X и обычно выровненный полного принципа разработки Mac OS X, описанного ранее.

  2. Какой сеанс входа в систему GUI, если таковые имеются, в настоящее время использует консоль? — Этому отвечают ниже.

Можно получить имя пользователя текущего пользователя консоли путем вызова SCDynamicStoreCopyConsoleUser функция (от платформы Конфигурации системы). Перечисление 1 показывает, как сделать это.

Перечисление 1  , Получающее текущего пользователя консоли

static CFStringRef CopyCurrentConsoleUsername(SCDynamicStoreRef store)
    // Returns the name of the current console user, or NULL if there is 
    // none. store may be NULL, in which case a transient dynamic store 
    // session is used.
{
    CFStringRef result;

    result = SCDynamicStoreCopyConsoleUser(store, NULL, NULL);

    // If the current console user is "loginwindow", treat that as equivalent 
    // to none.

    if ( (result != NULL) && CFEqual(result, CFSTR("loginwindow")) ) {
        CFRelease(result);
        result = NULL;
    }

    return result;
}

Когда эта установка изменяется, платформа Конфигурации системы также упрощает быть уведомленной. Перечисление 2 показывает, как сделать это.

Перечисление 2  , Обнаруживающее, когда изменяется та установка

#include <assert.h>

#include <SystemConfiguration/SystemConfiguration.h>

static CFStringRef CopyCurrentConsoleUsername(SCDynamicStoreRef store);
    // defined above

static CFStringRef gCurrentConsoleUser;

static void MyNotificationProc(
    SCDynamicStoreRef	store,
    CFArrayRef          changedKeys,
    void *              info
)
    // Called out of our runloop when the current console user value 
    // changes in the dynamic store. It's possible to get multiple 
    // redundant notifications, so we debounce the notification by checking 
    // for changes relative to gCurrentConsoleUser.
{
    #pragma unused(changedKeys)
    #pragma unused(info)
    CFStringRef         currentConsoleUser;
    Boolean             didChange;

    // Get the current console user.

    currentConsoleUser = CopyCurrentConsoleUsername(store);

    // See if it changed.

    didChange = (gCurrentConsoleUser != NULL) != (currentConsoleUser != NULL);
    if ( ! didChange && (gCurrentConsoleUser != NULL) ) {
        assert(currentConsoleUser != NULL); // because if it was NULL, 
                                            // didChange would already be true
        didChange = ! CFEqual(gCurrentConsoleUser, currentConsoleUser);
    }

    // If it did, log that fact and remember the current value.

    if (didChange) {
        if (gCurrentConsoleUser != NULL) {
            CFRelease(gCurrentConsoleUser);
        }
        gCurrentConsoleUser = currentConsoleUser;
        if (gCurrentConsoleUser != NULL) {
            CFRetain(gCurrentConsoleUser);
        }

        CFShow(gCurrentConsoleUser);
    }
}

int main(int argc, char **argv)
{
    #pragma unused(argc)
    #pragma unused(argv)
    Boolean             success;
    SCDynamicStoreRef   store;
    CFStringRef         key;
    CFArrayRef          keys;
    CFRunLoopSourceRef  rls;

    // Set up our connection to the dynamic store so that notifications are 
    // delivered by calling MyNotificationProc.

    store = SCDynamicStoreCreate(
        NULL, 
        CFSTR("com.apple.dts.ConsoleUser"), 
        MyNotificationProc, 
        NULL
    );
    assert(store != NULL);

    // Set it up to notify us when the console user value changes.

    key = SCDynamicStoreKeyCreateConsoleUser(NULL);
    assert(key != NULL);

    keys = CFArrayCreate(NULL, (const void **) &key, 1, &kCFTypeArrayCallBacks);
    assert(keys != NULL);

    success = SCDynamicStoreSetNotificationKeys(store, keys, NULL);
    assert(success);

    // Add it to the runloop.

    rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
    assert(rls != NULL);

    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);

    // Print the current value.

    gCurrentConsoleUser = CopyCurrentConsoleUsername(store);
    CFShow(gCurrentConsoleUser);

    // Run forever printing any changes.

    CFRunLoopRun();

    // Clean up code. This simple tool will never execute this code 
    // (because CFRunLoopRun will never return), but I've left it here 
    // in case you adapt the code for a more complex situation.

    if (gCurrentConsoleUser != NULL) {
        CFRelease(gCurrentConsoleUser);
    }
    CFRunLoopSourceInvalidate(rls);
    CFRelease(rls);
    CFRelease(keys);
    CFRelease(key);
    CFRelease(store);

    return EXIT_SUCCESS;
}


История версии документа


ДатаПримечания
14.04.2008

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

08.04.2002

Новый документ, что, как определить, зарегистрирован ли пользователь на консоли, и быть уведомленным относительно изменений.