From 2a18a8abeb3fdde8fdbf8eb96aea5a4ad186a328 Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Tue, 3 Mar 2009 01:57:48 +0000 Subject: [PATCH] client: Revise Apple idle time detection for compatibility with OS 10.6 svn path=/trunk/boinc/; revision=17432 --- checkin_notes | 8 ++++ client/hostinfo_unix.cpp | 89 +++++++++++++++++++++++++++++++++++----- lib/hostinfo.h | 11 ++++- 3 files changed, 95 insertions(+), 13 deletions(-) diff --git a/checkin_notes b/checkin_notes index ce3145bbe8..20bda68fb8 100644 --- a/checkin_notes +++ b/checkin_notes @@ -2506,3 +2506,11 @@ David 2 Mar 2009 sched/ sched_config.cpp,h sched_locality.cpp + +Charlie 2 Mar 2009 + - client: Revise Apple idle time detection for compatibility with OS 10.6. + + client/ + hostinfo_unix.cpp + lib/ + hostinfo.h diff --git a/client/hostinfo_unix.cpp b/client/hostinfo_unix.cpp index 7bc9b11492..892d0ceb43 100644 --- a/client/hostinfo_unix.cpp +++ b/client/hostinfo_unix.cpp @@ -125,7 +125,8 @@ extern "C" { } // extern "C" #endif -NXEventHandle gEventHandle = NULL; +mach_port_t gEventHandle = NULL; +#include "util.h" #endif // __APPLE__ #ifdef _HPUX_SOURCE @@ -1078,20 +1079,86 @@ inline bool user_idle(time_t t, struct utmp* u) { #ifdef __APPLE__ +// NXIdleTime() is an undocumented Apple API to return user idle time, which +// was implemented from before OS 10.0 through OS 10.5. In OS 10.4, Apple +// added the CGEventSourceSecondsSinceLastEventType() API as a replacement for +// NXIdleTime(). However, BOINC could not use this newer API when configured +// as a pre-login launchd daemon unless that daemon was running as root, +// because it could not connect to the Window Server. So BOINC continued to +// use NXIdleTime(). +// +// In OS 10.6, Apple removed the NXIdleTime() API. BOINC can instead use the +// IOHIDGetParameter() API in OS 10.6. When BOINC is a pre-login launchd +// daemon running as user boinc_master, this API works properly under OS 10.6 +// but fails under OS 10.5 and earlier. +// +// So we use weak-linking of NxIdleTime() to prevent a run-time crash from the +// dynamic linker, and use the IOHIDGetParameter() API if NXIdleTime does not +// exist. +// bool HOST_INFO::users_idle( bool check_all_logins, double idle_time_to_run, double *actual_idle_time ) { - double idleTime = 0; - - if (gEventHandle) { - idleTime = NXIdleTime(gEventHandle); - } else { - // Initialize Mac OS X idle time measurement / idle detection - // Do this here because NXOpenEventStatus() may not be available - // immediately on system startup when running as a deaemon. - gEventHandle = NXOpenEventStatus(); - } + static double init_time = 0; + static bool error_posted = false; + double idleTime = 0; + io_service_t service; + kern_return_t kernResult; + UInt64 params; + IOByteCount rcnt = sizeof(UInt64); + if (init_time == 0) init_time = dtime(); + + if (NXIdleTime) { // Use NXIdleTime API in OS 10.5 and earlier + if (gEventHandle) { + idleTime = NXIdleTime(gEventHandle); + } else { + // Initialize Mac OS X idle time measurement / idle detection + // Do this here because NXOpenEventStatus() may not be available + // immediately on system startup when running as a deaemon. + if ((dtime() - init_time) > 180) { // Limit retries to 180 seconds + + msg_printf(NULL, MSG_USER_ERROR, + "User idle detection is disabled: initialization failed." + ); + goto bail; + } + + gEventHandle = NXOpenEventStatus(); + } + } else { // NXIdleTime API does not exist in OS 10.6 and later + if (gEventHandle) { + kernResult = IOHIDGetParameter( gEventHandle, CFSTR(EVSIOIDLE), sizeof(UInt64), ¶ms, &rcnt ); + if ( kernResult != kIOReturnSuccess ) { + if (!error_posted) { + msg_printf(NULL, MSG_USER_ERROR, + "User idle time measurement failed because IOHIDGetParameter failed." + ); + error_posted = true; + } + goto bail; + } + idleTime = ((double)params) / 1000.0 / 1000.0 / 1000.0; + } else { + // When the system first starts up, allow time for HIDSystem to be available if needed + if ((dtime() - init_time) > 180) { // Limit retries to 180 seconds + + msg_printf(NULL, MSG_USER_ERROR, + "Could not connect to HIDSystem: user idle detection is disabled." + ); + goto bail; + } + + service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass)); + if (service == 0) goto bail; + + kernResult = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &gEventHandle); + if (kernResult != KERN_SUCCESS) goto bail; + + } // End gEventHandle == NULL + } // End NXIdleTime API does not exist + + bail: if (actual_idle_time) { *actual_idle_time = idleTime; } diff --git a/lib/hostinfo.h b/lib/hostinfo.h index 4f8312a4d8..f7a2974383 100644 --- a/lib/hostinfo.h +++ b/lib/hostinfo.h @@ -82,9 +82,16 @@ public: extern "C" { #endif #include +#include +#include + +// Apple has removed NxIdleTime() beginning with OS 10.6, so we must use +// weak linking to avoid a run-time crash. For details, please see the +// comments in the __APPLE__ version of HOST_INFO::users_idle() in +// client/hostinfo_unix.cpp. typedef mach_port_t NXEventHandle; -NXEventHandle NXOpenEventStatus(void); -extern double NXIdleTime(NXEventHandle handle); +NXEventHandle NXOpenEventStatus(void) __attribute__((weak_import)); +extern double NXIdleTime(NXEventHandle handle) __attribute__((weak_import)); #ifdef __cplusplus } // extern "C" #endif