Sign in to follow this  
Followers 0
jstump1

Scheduled Shutdown dilemma

7 posts in this topic

#1 ·  Posted (edited)

ScheduledShutdownAndSendWOL.zipHello -

I've started developing a script that records machine name, mac address, and subnet broadcast address to a file on a network share. This information is used by an AutoIT Wake-On-Lan packet generator to turn on the machines at a scheduled time. The script also shuts down the computer at a given time, but only if the user responds that it is OK to shut down or a timeout value is reached. That all works so far.

Issues:

The problem I have is that I also want the computer to shutdown if noone is logged into it. My assumption is that the script needs to run upon machine startup, so it needs to run as a service. Another option would be to use AT or the Windows Scheduler.

If someone turns the computer on after the shutdown time I want a message to popup (even before logon) allowing the user to cancel the shutdown that is about to happen. This is new territory for me so I could use some ideas on how to proceed. Maybe I need to rethink how to accomplish this?

Questions:

Is it possible for some type of prompt to appear before user logon for the user to answer? I've searched the forum and it doesn't look good, but my hope isn't squashed yet.

What local service account would be most appropriate for the script? I think NetworkService instead of LocalSystem if possible because it has fewer security privilidges, see "If the service opens a command window and runs a batch file, the user could hit CTRL+C to terminate the batch file and gain access to a command window with LocalSystem permissions."

http://msdn.microsoft.com/en-us/library/ms684272(VS.85).aspx

http://msdn.microsoft.com/en-us/library/ms684190(VS.85).aspx

Here's my script so far:

#Region AutoIt3Wrapper directives section
#AutoIt3Wrapper_Prompt=y
#AutoIt3Wrapper_Run_Debug_Mode=n
#AutoIt3Wrapper_UseUpx=y
#AutoIt3Wrapper_Allow_Decompile=n
#AutoIt3Wrapper_Res_Comment=record mac addresses and last network broadcast address
#AutoIt3Wrapper_Res_Description=get mac addresses
#AutoIt3Wrapper_Res_Fileversion=0.9.0.17
#AutoIt3Wrapper_Res_FileVersion_AutoIncrement=y
#AutoIt3Wrapper_Res_LegalCopyright=James Stump 2009
#AutoIt3Wrapper_Res_Field=Licence|GNU GPL V2 (http://www.gnu.org/copyleft/gpl.html)
#EndRegion

#NoTrayIcon

#include <String.au3>
#include <Misc.au3>
_Singleton("ScheduledShutdown")

Opt("MustDeclareVars", 0)


Const $wbemFlagReturnImmediately = 0x10
Const $wbemFlagForwardOnly = 0x20
Sleep(300000)
Sleep (Random (1, 900000, 1))
GetMac()
$ShutdownHour = IniRead ( @ScriptDir & "\ScheduledShutdown.ini", "Time", "ShutdownHour", "16" ) ;in hours ex: 17 for 5:00pm 02 for 2:00am
$ShutdownMinute = IniRead ( @ScriptDir & "\ScheduledShutdown.ini", "Time", "ShutdownMinute", "45" )  ;in minutes ex: 02 for 5:02
$ShutDownWindowTimeout = IniRead ( @ScriptDir & "\ScheduledShutdown.ini", "Time", "ShutDownWindowTimeout", "1800" )  ;in seconds ex: 1800 for a 30 minute delay
$ShutdownCountdown = IniRead ( @ScriptDir & "\ScheduledShutdown.ini", "Time", "ShutdownCountdown", "120" )  ;in seconds ex: 120 for a 2 minute delay
While @HOUR < ($ShutdownHour - 1)
    Sleep (1800000)
WEnd
While @HOUR < $ShutdownHour
    Sleep (300000)
WEnd
While @MIN < $ShutdownMinute
    Sleep (1000)
WEnd
GetMac ()
If ProcessExists ("explorer.exe") <> 0 Then
        $UserResponse = MsgBox (1, "Scheduled Shutdown", "Your computer is scheduled to shutdown now. Click CANCEL to keep working.", $ShutDownWindowTimeout)
    If $UserResponse = 1 or $UserResponse = -1 Then
        ShellExecute("shutdown.exe", "-s -f -t " & $ShutdownCountdown & " -d p:0:0")
    Else
        Sleep (3600000)
        $UserResponse = MsgBox (1, "Scheduled Shutdown", "Your computer is scheduled to shutdown now. Click CANCEL to keep working.", $ShutDownWindowTimeout)
        If $UserResponse = 1 or $UserResponse = -1 Then
            ShellExecute("shutdown.exe", "-s -f -t " & $ShutdownCountdown & " -d p:0:0")
        Else
            MsgBox (0, "Reminder:", "Please don't forget to shutdown this computer when you are done.", 1800)
        EndIf               
    EndIf
Else
    $UserResponse = MsgBox (1, "Scheduled Shutdown", "Your computer is scheduled to shutdown now. Click CANCEL to keep working.", $ShutDownWindowTimeout)
    If $UserResponse = 1 or $UserResponse = -1 Then
        ShellExecute("shutdown.exe", "-s -f -t " & $ShutdownCountdown & " -d p:0:0")
    Else
    EndIf
EndIf


; Converts a string containing an (IPv4) Internet Protocol dotted address into a proper address
Func ip2long($ip_str)
    Local $ip_arr = StringRegExp($ip_str, "(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})", 1)
    If @error Then
        SetError(1) ; incorrect address
        Return 0
    EndIf
    Return BitOR( _
            BitShift($ip_arr[0], -24), _
            BitShift($ip_arr[1], -16), _
            BitShift($ip_arr[2], -8), _
            $ip_arr[3])
EndFunc   ;==>ip2long


; Converts an (IPv4) Internet network address into a string in Internet standard dotted format
Func long2ip($ip)
    Return BitAND(BitShift($ip, 24), 255) & "." & _
            BitAND(BitShift($ip, 16), 255) & "." & _
            BitAND(BitShift($ip, 8), 255) & "." & _
            BitAND($ip, 255)
EndFunc   ;==>long2ip

Func GetMac()
    $ServerShare = IniRead ( @ScriptDir & "\ScheduledShutdown.ini", "Files", "SaveFolder", @ScriptDir )
    ; record subnet and mac address on each ip enabled local interface
    Local $objWMIService = ObjGet("winmgmts:\\.\root\CIMV2")
    Local $colItems = $objWMIService.ExecQuery ("SELECT IPAddress, IPSubnet, MACAddress, Description FROM Win32_NetworkAdapterConfiguration " & _
            "WHERE IPEnabled<>0", "WQL", BitOR($wbemFlagReturnImmediately, $wbemFlagForwardOnly))

    If IsObj($colItems) Then

        For $objItems In $colItems
            With $objItems
                Local $ip = ip2long(.IPAddress (0))
                If $ip <> 0 Then
                    Local $subnet = ip2long(.IPSubnet (0))
                    Local $broadcast_str = long2ip(BitOR($ip, BitNOT($subnet)))
                    Local $mac_address = StringRegExpReplace(.MACAddress(0), ":", "")
;                   MsgBox(0, "Mac Address, Broadcast Address, Description", $mac_address & ", " & $broadcast_str & ", " & .Description())
                    If FileExists ($ServerShare & "\" & $broadcast_str & "\" & @ComputerName & ".csv") Then
                        $FileTime = FileGetTime ($ServerShare & "\" & $broadcast_str & "\" & @ComputerName & ".csv")
                        If $FileTime[2] <> @MDAY Then
                            FileDelete ($ServerShare & "\" & $broadcast_str & "\" & @ComputerName & ".csv")
                            Else
                        EndIf
                        Else
                    EndIf   
                    ;Create network folder if it does not exist (mode 8). Append to end of file (mode 1). 8+1=9
                    Local $AddressFile = FileOpen($ServerShare & "\" & $broadcast_str & "\" & @ComputerName & ".csv", 9)
                    FileWriteLine($AddressFile, $mac_address & "," & $broadcast_str)
                    FileClose($AddressFile)
                EndIf
            EndWith
        Next
    EndIf
EndFunc    ;==>GetMac()
Edited by jstump1

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

I think you don't need a service. Just schedule a program to run onstart using schtasks /create /sc onstart. This program then could check the time and if it is late already it would check if the explorer.exe is running and if not then shutdown the pc. If yes then it can schedule another program via the AT /interactive command - that would be visible for a user - which would ask user if it is OK to shut down the machine. I don't know how to display something to the logon screen but I am sure it is possible somehow because I have seen some software having its window or logo on the logon screen... The program would not be scheduled only onstart but also every day at the time you want the pcs to shutdown. I guess it could work this way.

Edited by LoWang

Share this post


Link to post
Share on other sites

I think you don't need a service. Just schedule a program to run onstart using schtasks /create /sc onstart. This program then could check the time and if it is late already it would check if the explorer.exe is running and if not then shutdown the pc. If yes then it can schedule another program via the AT /interactive command - that would be visible for a user - which would ask user if it is OK to shut down the machine. I don't know how to display something to the logon screen but I am sure it is possible somehow because I have seen some software having its window or logo on the logon screen... The program would not be scheduled only onstart but also every day at the time you want the pcs to shutdown. I guess it could work this way.

Yes, I think you are right on a couple of points. It could start as a scheduled task upon bootup - that would work around the running-as-a-service issue. I haven't tested it but the shutdown command should work without a user logged in. If it doesn't I could look at finding a direct Windows shutdown interface (API, .dll, etc.) instead.

I guess it also needs to be scheduled to start just after midnight every day, that way if the user ignored the shutdown prompts and left the computer on the script starts up again the next day.

Issues: If a person (or IT worker) turns a PC on between my scheduled shutdown time & midnight the script would want to shutdown the computer. I guess my initial script delay of 5 to 20 minutes after script start might take care of that.

What if I want to install software/patches/reconfig during the normal shutdown time though? I need some type of bypass mechanism that is centrally controlled...I'll work on that Monday. Probably another variable with a local .ini file pointing to a remote .ini file for shutdown time, delays, etc.

I guess another option is for the windows scheduler to call a network location. I don't have a lot of faith in that though - the network may not be available before it tries to start.

Thanks,

Stumppc

Share this post


Link to post
Share on other sites

shutdown command works regardless of user logged on or not, you can test it via psexec for example.

I am not really sure what do you mean by that installing you want to do during shutdown time. You mean that sometimes you would go there personally, poweron the pc and install something on it? Or logon via remote desktop? In that case make the shutdown program not shut down the computer if certain user is logged on (it would be administrator I guess) There is a way to detect who is logged on. I can check it for you if you want.

Or if you would make the installation via some remote scripting or whatever, you can make it first create some file like c:\installation.lck somewhere and the shutdown script would not shutdown the pc until the file is deleted when the installation is finished.

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

This function detects which user is interactively logged on the computer:

Func username()
 ; Generated by AutoIt Scriptomatic
 $wbemFlagReturnImmediately = 0x10
 $wbemFlagForwardOnly = 0x20
 $colItems = ""
 $strComputer = "localhost"
 $Output=""
 $objWMIService = ObjGet("winmgmts:\\" & $strComputer & "\root\CIMV2")
 $colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_ComputerSystem", "WQL", _
                                          $wbemFlagReturnImmediately + $wbemFlagForwardOnly)
 If IsObj($colItems) then
   For $objItem In $colItems
       $Output = $objItem.UserName
   Next
 Else
   $output=-1
 Endif
 return $output
endfunc
Edited by LoWang

Share this post


Link to post
Share on other sites

Nice - I like that function. I can think of several uses for it. If I bundle it with psexec during inventory time I can double-check who is using what computer against my SMS database. If I get a service ticket I could check to see if the user is actually logged in before starting a remote control session. If they aren't logged in I wouldn't bother calling first before starting my work.

I guess I didn't explain myself enough about keeping PCs on instead of shutting down at times. I work in a large network environment with many other IT folks, buildings everywhere all over the state (state government). So let's say a person wants to keep PCs on over the weekend to install a service pack. They'll need to install all the service packs (for us usually scheduled via MS SMS Server 2003 or WSUS) and then visit the computers who have some type of error over the weekend. In that instance I need a way to disable the shutdown mechanism centrally. If it is disabled by subnet that would be best.

I have some ideas but no time to work on it until Monday...

Thanks

~Stumppc

Share this post


Link to post
Share on other sites

ehm now that I read your reply I realize I forgot about psloggedon.exe :) Do you know that utility? It shows you who is logged on some pc. Now that I tested it I see it shows the logged user on a local computer as well, so that's another possibility besides that function.

If you really need to disable the shutdown script centrally, you will have to make it communicate with some server and check if the shutdown is permitted. The easiest way would be probably to use inetget function to read some file (ini file would be easiest to use with AutoIt) from a web page and there could be either just shutdown=true/false or something like shutdowndeny=10.20.100.0 to mark subnet where you don't want pcs to be shut down. I am currently working on a similar project so I cope with it as well :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0