|
Q: How do I tell whether a console user is currently logged in?A: It really depends on what you mean by "currently logged in". With the introduction of fast user switching in Mac OS X 10.3, multiple users can be logged into the system at the same time. So, you have to split the question in two:
These are clear questions with clear answers (see below). However, it's likely that, if you're asking these questions, you need to rethink your architecture. One of the fundamental design principles of the Mac OS X is that high-level services can depend on low-level services, but not the other way around. For example, it's fine for an application to depend on the kernel, but it's a problem if the kernel depends on an application. The question of determining the current console user typically arises when you break this design principle. Only application-level services should care whether a user's session is currently active on the console, and it's easy for such services to determine that. On the other hand, a daemon should not be concerned about whether a user is logged in on the console, and thus it's tricky to get this information from that context. This issue is discussed in great depth in Technical Note TN2083, 'Daemons and Agents' and, specifically, in the "Design Considerations" section of that technote. So, before you read the rest of this document, I strongly recommend that you read that technote and think about how you can align your architecture with the overall Mac OS X architecture. WARNING: The System Configuration framework mechanism for determining the current console user has a number of important caveats:
Because of these issues, routines like If you choose to ignore all of the warnings above, here are the answers to your questions:
You can get the user name of the current console user by calling the Listing 1: Getting the current console user 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; } The System Configuration framework also makes it easy to be notified when this setting changes. Listing 2 shows how to do that. Listing 2: Discovering when that setting changes #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; } Document Revision History
Posted: 2008-04-14 |
|