#tag Class Protected Class IdleLogout Inherits Application #tag Event Sub Open() Dim debugFileName as string Dim theDate as date dim mXMLtreeToFollow(-1) as string dim mPrefKeyFoundData(-1) as string dim mTempFoundPlistData as string = "" // Local variable to use for gathering plist settings theDate = new Date Globals.gAppFolderItem = GetFolderItem("/Users/Shared/", FolderItem.PathTypeShell) if Globals.gAppFolderItem.Exists = false then Globals.gAppFolderItem = GetFolderItem("/tmp/", FolderItem.PathTypeShell) MsgBox "Can't find /Users/Shared! Using /tmp/ for logs" end // Set the name of the log debugFileName = "psuIdleLogout.RUN" + MiscMethods.PadData("0",2,str(theDate.Year),Globals.kLogToFileDisable) _ + "-" + MiscMethods.PadData("0",2,str(theDate.Month),Globals.kLogToFileDisable) + "-" debugFileName = debugFileName + MiscMethods.PadData("0",2,str(theDate.Day),Globals.kLogToFileDisable) + _ "-" + MiscMethods.PadData("0",2,str(theDate.Hour),Globals.kLogToFileDisable) + "-" debugFileName = debugFileName + MiscMethods.PadData("0",2,str(theDate.Minute),Globals.kLogToFileDisable) + _ "-" + MiscMethods.PadData("0",2,str(theDate.Second),Globals.kLogToFileDisable) + ".log" // Initilize the log, quit if we can't create the log if not (LogToFile.Initialize(debugFileName,Globals.gAppFolderItem)) then beep MsgBox "Error creating run log file! Exiting..." quit end if // Keep only the last 5 logs if ( LogToFile.DeleteOldLogs(5) ) then LogToFile("Deleted Old Logs") end if // CFPref calls 'dim ComputerIdleAfterNumSeconds as Integer = "900" 'dim IdleLoopDelaySeconds as Integer = "60" 'dim WaitForUserPromptSeconds = "90" // Set version number in pref file // prefs.Value ("version") = App.pAppVersion // dim AppVersionPref as String = prefs.Value("version", App.pAppVersion) // Get version from plist file 'dim ComputerIdleAfterNumSecondsPref as string = prefs.Value("ComputerIdleAfterNumSeconds", App.pComputerIdleAfterNumSeconds) 'dim IdleLoopDelaySecondsPref as string = prefs.Value("IdleLoopDelaySeconds", App.pIdleLoopDelaySeconds) 'dim WaitForUserPromptSecondsPref as string = prefs.Value("WaitForUserPromptSeconds", App.pWaitForUserPromptSeconds) ' 'LogToFile("CFPrefs Return of ComputerIdleAfterNumSeconds: " + ComputerIdleAfterNumSecondsPref ) // Are there any users that we should ignore running for? if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "IgnoreUser", mTempFoundPlistData ) ) then pIgnoreUser = mTempFoundPlistData LogToFile(CurrentMethodName + ": Found the default key data, = " + str(pIgnoreUser) ) else // Failed! LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'IgnoreUser', using default of "+ str (pIgnoreUser) ) end if // Are there any groups that we should ignore running for? if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "IgnoreGroup", mTempFoundPlistData ) ) then pIgnoreGroup = mTempFoundPlistData LogToFile(CurrentMethodName + ": Found the default key data, = " + str(pIgnoreGroup) ) else // Failed! LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'IgnoreGroup', using default of "+ str (pIgnoreGroup) ) end if // Is this user macadmin? if ( MiscMethods.CurrentUsername(pIgnoreUser) ) then // It is macadmin, quit the app LogToFile("Current user is " + pIgnoreUser + ", quiting app") quit else LogToFile("User is not " + pIgnoreUser + ", continuing") end if // Is this user an admin? if ( MiscMethods.CurrentGroup(pIgnoreGroup) ) then // It is macadmin, quit the app LogToFile("Current user is an " + pIgnoreGroup + ", quiting app") //[p=quit else LogToFile("User is not an " + pIgnoreGroup + ", continuing") end if // What should the Title Be in the Popup Window if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "WindowTitle", mTempFoundPlistData ) ) then LogoutWarning.WarningTitle.setString(str(mTempFoundPlistData)) LogToFile(CurrentMethodName + ": Found the default key data, = " + str(mTempFoundPlistData) ) else // Failed! LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'WindowTitle', using default of "+ str(LogoutWarning.WarningTitle.Text)) end if // How long should we wait before considering the Mac is idle too long? if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "ComputerIdleAfterNumSeconds", mTempFoundPlistData ) ) then pComputerIdleAfterNumSeconds = val( mTempFoundPlistData ) LogToFile(CurrentMethodName + ": Found the default key data, = " + str(pComputerIdleAfterNumSeconds) ) else // Failed! LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'ComputerIdleAfterNumSeconds', using default of "+ str (pComputerIdleAfterNumSeconds) ) end if // How often should we check to see how long the Mac has been idle? if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "IdleLoopDelaySeconds", mTempFoundPlistData ) ) then pIdleLoopDelaySeconds = val( mTempFoundPlistData ) LogToFile(CurrentMethodName + ": Found the default key data, = " + str(pIdleLoopDelaySeconds) ) else // Failed! LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'IdleLoopDelaySeconds', using default of "+ str (pIdleLoopDelaySeconds) ) end if // How long should we wait for the user to respond for More Time or to Log Out? if ( PlistHelper.readPlist(pDefaultPrefsFSPath, pDefaultPrefsFileName, "WaitForUserPromptSeconds", mTempFoundPlistData ) ) then pWaitForUserPromptSeconds = val( mTempFoundPlistData ) LogToFile(CurrentMethodName + ": Found the default key data, = " + str(pWaitForUserPromptSeconds) ) else // Failed! LogToFile(CurrentMethodName + ": Warning! Failed to find the default key 'WaitForUserPromptSeconds', using default of "+ str (pWaitForUserPromptSeconds) ) end if // Hide window from view at start LogoutWarning.Hide() // Start IdleThread LogToFile(CurrentMethodName + ": Running IdleThread") // start thread to watch for idle StartIdleWatch() // exit app now LogToFile(CurrentMethodName + ": <---") End Sub #tag EndEvent #tag Method, Flags = &h0 Sub forceLogout() LogToFile(CurrentMethodName + ": --->") // Testing mode, use say instead of logout Dim s As Shell s=New Shell dim ShellResults as string s.Mode = 0 // Log LogToFile(CurrentMethodName + ": START Killing Apps") // kill the apps that are open from /Apps LogToFile(CurrentMethodName + ": Killing all launched from /Applications") // s.Execute("kill -9 `ps axxx | grep ""/Applications"" | awk '{print $1}'`") ShellResults = s.Result LogToFile(CurrentMethodName + ": App Kill Results: " + ShellResults) // Kill PSU specific Applescript that can cause issues LogToFile(CurrentMethodName + ": Killing the psuStartupManger") //s.Execute("kill -9 `ps axxx | grep ""/Library/CLMshared/Startup Items/psuStartupManager.app"" |grep applet | awk '{print $1}'`") ShellResults = s.Result LogToFile(CurrentMethodName + ": StartupManager Kill Results: " + ShellResults) // Log out our user with force LogToFile(CurrentMethodName + ": Tell AppleScript to log it out") // New method to logout with osascript. //s.Execute("osascript -e 'tell application ""System Events"" to keystroke ""q"" using {command down, shift down, option down}'") ShellResults = s.Result LogToFile(CurrentMethodName + ": LogOut AS Results: " + ShellResults) // Log and quit LogToFile(CurrentMethodName + ": <---") Quit End Sub #tag EndMethod #tag Method, Flags = &h0 Function IdleSeconds() As Integer // Set up variables for idle time // Shell result Dim mIdleSecs As String // Shell exit code Dim merrCode As Integer // Set up shell Dim s As Shell s=New Shell s.Mode = 0 // Check idle time //s.Execute code to check idle time from USB input devices s.Execute "/bin/echo $((`/usr/sbin/ioreg -c IOHIDSystem | /usr/bin/sed -e '/HIDIdleTime/ !{ d' -e 't' -e '}' -e 's/.* = //g' -e 'q'` / 1000000000))" // Set results to mIdleResult mIdleSecs = s.Result // Set error code to mIdleError merrCode = s.ErrorCode // Log mIdleResult for debugging // System.Log(System.LogLevelError, "Method: " + midleSecs) // LogToFile("mIdleSeconds: " + str(mIdleSecs)) Return val(mIdleSecs) End Function #tag EndMethod #tag Method, Flags = &h0 Function makeFolder(mkdirPath As string) As Boolean // Set up variables for idle time // Shell result = mkdirPath // Shell exit code Dim merrCode As Integer // Set up shell Dim s As Shell s=New Shell s.Mode = 0 // Check idle time //s.Execute code to check idle time from USB input devices s.Execute "mkdir -p /Users/Shared" // Set results to mIdleResult mkdirPath = s.Result // Set error code to mIdleError merrCode = s.ErrorCode // Log mIdleResult for debugging // System.Log(System.LogLevelError, "Method: " + midleSecs) // LogToFile("mIdleSeconds: " + str(mIdleSecs)) if mkdirPath = "0" then return true else return false end End Function #tag EndMethod #tag Method, Flags = &h0 Function RemoteControlCheck() As Boolean // Set up variables for idle time Dim stablished as String = "ESTABLISHED" // Shell result Dim rccheck As String // Shell exit code Dim merrCode As Integer // Set up shell Dim s As Shell s=New Shell s.Mode = 0 // Check idle time //s.Execute code to check for connection s.Execute "/usr/sbin/netstat -n | /usr/bin/grep '.5900'| /usr/bin/awk '{print $6}'" // Set results to rccheck // Make the entire returned text uppercase in case Apple ever changes the case to CaMeL CaSe, which would break our code: rccheck = Uppercase(s.Result) //LogToFile("rccheck: " + rccheck) // Set error code to merrCode merrCode = s.ErrorCode // LogToFile("rccheck: "+ rccheck) // LogToFile("stablished: "+ stablished) If ( InStr( rccheck, stablished ) > 0 ) then return true else //LogToFile("rccheck: false") return false End if // System.Log(System.LogLevelError, "Method: " + midleSecs) // LogToFile("mIdleSeconds: " + str(mIdleSecs)) End Function #tag EndMethod #tag Method, Flags = &h0 Sub StartIdleWatch() // if the new WatchForIdleThread is not created, do it now if ( pWatchForIdleThread = nil ) then // instatiate new thread with enumerated type pWatchForIdleThread = _ new WatchForIdleThread(Int32(eTaskType.StartIdleWatch )) // add custom methods for run and finished handlers AddHandler pWatchForIdleThread.Run, WeakAddressOf Thread_WaitForIdleTime AddHandler pWatchForIdleThread.Finished, WeakAddressOf Thread_Finished end if // Run the thread if it's not already running: if ( pWatchForIdleThread.State <> Thread.Running ) then // Hide window during idle watch LogoutWarning.Hide() // Set label to default countdown time LogoutWarning.TimeLabel.setString(str(pWaitForUserPromptSeconds)) // run the new thread pWatchForIdleThread.Run end if End Sub #tag EndMethod #tag Method, Flags = &h0 Sub StartUserCountDown() // if the new WatchForIdleThread is not created, do it now if ( pIdleCountDownThread = nil ) then // instatiate new thread with enumerated type pIdleCountDownThread = _ new WatchForIdleThread(Int32(eTaskType.StartUserCountDown )) // add custom methods for run and finished handlers AddHandler pIdleCountDownThread.Run, WeakAddressOf Thread_WaitForUserInput AddHandler pIdleCountDownThread.Finished, WeakAddressOf Thread_Finished end if // Run the thread if it's not already running if ( pIdleCountDownThread.State <> Thread.Running ) then // run the new thread pIdleCountDownThread.Run end if End Sub #tag EndMethod #tag Method, Flags = &h21 Private Sub Thread_Finished(paramThread as WatchForIdleThread) LogToFile(CurrentMethodName + ": --->") // Kill the thread that was used to get here paramThread.Kill // Found out which thread just finished select case Int32 ( paramThread.pTaskType ) // If the StartIdleWatch thread just finished case int32( eTaskType.StartIdleWatch ) // LogToFile(CurrentMethodName + ": Idle time has expired.") // Show Login Window LogoutWarning.Show() // start logout countdown window StartUserCountDown() case int32 ( eTaskType.StartUserCountDown ) // LogToFile(CurrentMethodName + ": No user response from countdown.") // LogToFile(CurrentMethodName + ": LogoutWarning.pMoreTimeAskedFor = " +str(pMoreTimeAskedFor)) // If the user (or anyone) asked for more time if (pMoreTimeAskedFor) then // if the user asks for more time, start the idle watch thread again // Restart Idle Watch StartIdleWatch() else // if the user ran out of time, start the logout // kill user logins forceLogout() end if else end select End Sub #tag EndMethod #tag Method, Flags = &h21 Private Sub Thread_WaitForIdleTime(paramThread as WatchForIdleThread) // Set up new integer to store idle seconds Dim mIdleResult As Integer // get current idle seconds mIdleResult = IdleSeconds() // LogToFile(CurrentMethodName + " Idle Seconds: " + str(mIdleResult)) // LogToFile(CurrentMethodName + " waiting for " + str(LogoutWarning.pComputerIdleAfterNumSeconds)) // While our idle result is less than max time while ( mIdleResult < pComputerIdleAfterNumSeconds ) // Wait pIdleLoopDelaySeconds each loop App.SleepCurrentThread( pIdleLoopDelaySeconds * 1000 ) // Check idle time: mIdleResult = IdleSeconds() // Log our time idle LogToFile(CurrentMethodName + ": Idle Seconds: " + str(mIdleResult)) wend // while there is a ARD/VNC session, don't idle out (netstat -n | grep '.5900') while (RemoteControlCheck() ) LogToFile(CurrentMethodName + ": Someone is controlling via ARD/VNC, waiting...") wend // When there is not remote control LogToFile(CurrentMethodName + ": No one is controlling via ARD/VNC, continuing...") End Sub #tag EndMethod #tag Method, Flags = &h21 Private Sub Thread_WaitForUserInput(paramThread as WatchForIdleThread) LogToFile(CurrentMethodName + ": --->") // Set how long to wait before logging out user, set from global variable for LogOutDelay Dim pLogoutDelay as Integer = pWaitForUserPromptSeconds // While we're waiting for the countdown to finish... While pLogoutDelay >= 0 // LogToFile(CurrentMethodName + "Time Left: " + str(pLogoutDelay)) // Set global variable to the time left pCountDownTime = pLogoutDelay // call updateUI to set countdown label pIdleCountDownThread.UpdateUI() // take a second away from the time left pLogoutDelay = pLogoutDelay - 1 // do nothing for 1 second App.SleepCurrentThread( 1000 ) // sleep 1 second Wend // If we're here, the user didn't cancel or log out manaully // user did not ask for more time pMoreTimeAskedFor = false // Log LogToFile(CurrentMethodName + ": Time Out for Response. Log User Out...") // Done here LogToFile(CurrentMethodName + ": <---") // Return to Thread_Finished End Sub #tag EndMethod #tag Note, Name = Icon Application Icon use with CC license: From http://www.flickr.com/photos/23453447@N02/5107438855/sizes/o/in/photostream/ By zyrquel http://www.flickr.com/photos/23453447@N02/ #tag EndNote #tag Property, Flags = &h0 pAppVersion As String = "2.0" #tag EndProperty #tag Property, Flags = &h0 pComputerIdleAfterNumSeconds As Integer = 900 #tag EndProperty #tag Property, Flags = &h0 pCountDownTime As Integer #tag EndProperty #tag Property, Flags = &h0 pDefaultPrefsFileName As String = "edu.psu.its.clc.IdleLogoutSettings.plist" #tag EndProperty #tag Property, Flags = &h0 pDefaultPrefsFSPath As String = "/Library/CLMadmin/Config/" #tag EndProperty #tag Property, Flags = &h0 pIdleCountDownThread As WatchForIdleThread #tag EndProperty #tag Property, Flags = &h0 pIdleLoopDelaySeconds As Integer = 60 #tag EndProperty #tag Property, Flags = &h0 pIgnoreGroup As String = "admin" #tag EndProperty #tag Property, Flags = &h0 pIgnoreUser As String = "macadmin" #tag EndProperty #tag Property, Flags = &h0 pMoreTimeAskedFor As Boolean = true #tag EndProperty #tag Property, Flags = &h0 pWaitForUserPromptSeconds As Integer = 90 #tag EndProperty #tag Property, Flags = &h0 pWatchForIdleThread As WatchForIdleThread #tag EndProperty #tag Constant, Name = kEditClear, Type = String, Dynamic = False, Default = \"&Delete", Scope = Public #Tag Instance, Platform = Windows, Language = Default, Definition = \"&Delete" #Tag Instance, Platform = Linux, Language = Default, Definition = \"&Delete" #tag EndConstant #tag Constant, Name = kFileQuit, Type = String, Dynamic = False, Default = \"&Quit", Scope = Public #Tag Instance, Platform = Windows, Language = Default, Definition = \"E&xit" #tag EndConstant #tag Constant, Name = kFileQuitShortcut, Type = String, Dynamic = False, Default = \"", Scope = Public #Tag Instance, Platform = Mac OS, Language = Default, Definition = \"Cmd+Q" #Tag Instance, Platform = Linux, Language = Default, Definition = \"Ctrl+Q" #tag EndConstant #tag Enum, Name = eTaskType, Type = Int32, Flags = &h0 StartIdleWatch StartUserCountDown #tag EndEnum #tag ViewBehavior #tag ViewProperty Name="pAppVersion" Group="Behavior" InitialValue="1.1" Type="String" EditorType="MultiLineEditor" #tag EndViewProperty #tag ViewProperty Name="Untitled" Group="Behavior" Type="Integer" #tag EndViewProperty #tag EndViewBehavior End Class #tag EndClass