LGP Startup/Shutdown Script: Difference between revisions
(Initial ALPHA Release) |
(→Origin: Typo) |
||
Line 7: | Line 7: | ||
== Origin == | == Origin == | ||
I needed to deploy the nVidia driver update to the systems within my company. To make the process as painless as possible, I wanted the process to run before the user logs into the system as the install requires a reboot. By using the Local Group Policy Scripting service, we are able to perform the update without | I needed to deploy the nVidia driver update to the systems within my company. To make the process as painless as possible, I wanted the process to run before the user logs into the system as the install requires a reboot. By using the Local Group Policy Scripting service, we are able to perform the update without forcing the user to log on twice due to reboot. This also further prevents the user from interrupting the update. | ||
== Purpose == | == Purpose == |
Revision as of 13:16, 11 June 2009
Author: Zinthose (talk, contrib) |
UNSTABLE RELEASE | This release is in development and should not be used in a production environment. |
Origin
I needed to deploy the nVidia driver update to the systems within my company. To make the process as painless as possible, I wanted the process to run before the user logs into the system as the install requires a reboot. By using the Local Group Policy Scripting service, we are able to perform the update without forcing the user to log on twice due to reboot. This also further prevents the user from interrupting the update.
Purpose
Intended for use in cooperate environments with a deployment system like Symantec's Altiris or Microsoft's System Management Servers.
!! Caution !!
The scripts need to be removed after execution. This has the potential to enter a continuous reboot cycle if installing software that reboots after completion but fails to remove the LGP Script entry. If you get into this situation, you will need to boot the system into safe mode and remove the entries manually. Removal can be done by modifying the Registry and the script.ini files manually, running a previously generated "removal" NSI script, or using the GPEdit.msc utility.
DO NOT ATTEMPT TO USE THIS IF YOU DON'T UNDERSTAND WHAT THE LOCAL GROUP POLICY SCRIPTS ARE AND DO.
USE AT YOUR OWN RISK!
Functions
${LGPScript::Create} Mode = 'Startup' Or 'Shutdown' Command = ${PathToExecutable} Parameters = ${ParametersForCommand} ReturnCode = 0 = 'Success' Or -1 = 'Failed' ${LGPScript::Remove} Mode = 'Startup' Or 'Shutdown' Command = ${PathToExecutable} Parameters = ${ParametersForCommand} ReturnCode = 0 = 'Success' Or -1 = 'Failed' ${LGPScript::RemoveAll} Mode = 'Startup' Or 'Shutdown' ReturnCode = 0 = 'Success' Or -1 = 'Failed'
Code
Eample
!include LGPScript.nsh RequestExecutionLevel Admin OutFile LGPScript_Example.exe ShowInstDetails show ### Main Section -Main ### Command to Run StrCpy $0 "$WINDIR\Notepad.exe" ### Get the short pathname, as spaces break the LGP scripts GetFullPathName /SHORT $0 $0 ### Prompt to remove any existing LGP scripts MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2|MB_SETFOREGROUND|MB_TOPMOST \ "Do you want to remove all existing Local Group Policy Scripts?" \ /SD IDNO IDNO SkipRemoveAll ### Remove Existing Startup Scripts ${LGPScript::RemoveAll} 'Startup' $1 DetailPrint "RemoveAll startup LGP entries:$1" ### Remove Existing Shutdown Scripts ${LGPScript::RemoveAll} 'Shutdown' $1 DetailPrint "RemoveAll shutdown LGP entries:$1" SkipRemoveAll: ### Prompt to add the sample LGP script MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2|MB_SETFOREGROUND|MB_TOPMOST \ "Do you want to add notepad to the startup LGP?" \ /SD IDNO IDNO SkipStartup ### Add Startup Script ${LGPScript::Create} 'Startup' '$0' '' $R1 DetailPrint "Create startup LGP return code:$R1" SkipStartup: ### Prompt to add the sample LGP script MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2|MB_SETFOREGROUND|MB_TOPMOST \ "Do you want to add notepad to the shutdown LGP?" \ /SD IDNO IDNO SkipShutdown ### Add Shutdown Script ${LGPScript::Create} 'Shutdown' '$0' '' $R1 DetailPrint "Create shutdown LGP return code:$R1" SkipShutdown: DetailPrint "Reboot this system to see the results..." SectionEnd
NSIS Library
; --------------------- ; LGPScript.nsh ; --------------------- ; ; Allows for execution of an application at system startup just prior ; to user Logon using the Local Group Policy (LGP) Service. ; !warning "ALPHA Release - Not Fully Tested" !warning "Known Issues:$\n* OS Version Checks need added$\n* Error Checking is incomplete" !ifndef ___LGPSCRIPT__NSH___ !define ___LGPSCRIPT__NSH___ !define _LGPScript_INIPATH '$SYSDIR\GroupPolicy\Machine' !define _LGPScript_INIFILE '${_LGPScript_INIPATH}\Scripts\scripts.ini' !define _LGPScript_REGPATH 'SOFTWARE\Policies\Microsoft\Windows\System\Scripts' !include x64.nsh !include LogicLib.nsh !include Registry.nsh # Available at: http://nsis.sourceforge.net/Registry_plug-in ### Public Function Macros !define LGPScript::Create "!insertmacro _LGPScript_Create" !macro _LGPScript_Create Mode Command Parameters ReturnCode Push "${Mode}" Push "${Command}" Push "${Parameters}" Call _LGPScript_Create Pop ${Returncode} !macroend !define LGPScript::Remove "!insertmacro _LGPScript_Remove" !macro _LGPScript_Remove Mode Command Parameters ReturnCode Push "${Mode}" Push "${Command}" Push "${Parameters}" Call _LGPScript_Remove Pop ${ReturnCode} !macroend !define LGPScript::RemoveAll "!insertmacro _LGPScript_RemoveAll" !Macro _LGPScript_RemoveAll Mode ReturnCode Push "${Mode}" Call _LGPScript_RemoveAll Pop ${ReturnCode} !macroend ### Internal Function Macros !define LGPScript::_SaveToINI "!insertmacro _LGPScript_SaveToINI" !macro _LGPScript_SaveToINI Mode Push "${Mode}" Call _LGPScript_SaveToINI !macroend !define LGPScript::_GetLGPRegPath "!insertmacro _LGPScript_GetLGPRegPath" !macro _LGPScript_GetLGPRegPath Mode RegPath Push "${Mode}" Call _LGPScript_GetLGPRegPath Pop ${RegPath} !macroend !define LGPScript::_ReindexLGP "!insertmacro _LGPScript_ReindexLGP" !macro _LGPScript_ReindexLGP Mode ReturnCode Push "${Mode}" Call _LGPScript_ReindexLGP Pop ${ReturnCode} !macroend !define LGPScript::_CreateLGP "!insertmacro _LGPScript_CreateLGP" !macro _LGPScript_CreateLGP RegPath ReturnCode Push "${RegPath}" Call _LGPScript_CreateLGP Pop ${ReturnCode} !macroend !define LGPScript::_Find "!insertmacro _LGPScript_Find" !macro _LGPScript_Find Mode Command Parameters RegPath Push "${Mode}" Push "${Command}" Push "${Parameters}" Call _LGPScript_Find Pop ${RegPath} !macroend ### Public Functions Function _LGPScript_RemoveAll ; (Mode) (ReturnCode) ClearErrors ## Preserve registers ; Stack: Mode Exch $0 ; Stack: $0 Push $1 ; Stack: $1 $0 Push $2 ; Stack: $2 $1 $0 ; $0 = Mode ## Get LGP Path ${LGPScript::_GetLGPRegPath} $0 $1 ${if} $1 == -1 ## Nothing to delete StrCpy $0 0 Goto FunctionExit ${endif} ## Remove Registry Key ${registry::KeyExists} "HKLM\$1" $2 ${If} $2 == -1 ## Nothing to delete StrCpy $0 0 Goto FunctionExit ${EndIf} ${registry::DeleteKey} "HKLM\$1" $2 ${If} $2 == -1 ## Error Deleting Key StrCpy $0 -1 Goto FunctionExit ${EndIf} ## Windows x64? ${If} ${RunningX64} ${DisableX64FSRedirection} ${EndIf} ## Remove from INI DeleteINISec "${_LGPScript_INIFILE}" $0 ## Windows x64? ${If} ${RunningX64} ${EnableX64FSRedirection} ${EndIf} ${If} ${Errors} ## Error Deleting INI Section StrCpy $0 -1 Goto FunctionExit ${EndIf} StrCpy $0 0 FunctionExit: ## Restore Registers ; Stack: $2 $1 $0 Pop $2 ; Stack: $1 $0 Pop $1 ; Stack: $0 Exch $0 ; Stack: <ReturnCode> FunctionEnd Function _LGPScript_Remove ; (Mode, Command, Parameters) (ReturnCode) ClearErrors ## Preserve registers ; Stack: Parameters Command Mode Exch $0 ; Stack: $0 Command Mode Exch 2 ; Stack: Mode Command $0 Exch $1 ; Stack: $1 Command $0 Exch ; Stack: Command $1 $0 Exch $2 ; Stack: $2 $1 $0 ; $0 = Parameters ; $1 = Mode ; $2 = Command ## Search for LGP Script ${LGPScript::_Find} $1 $2 $0 $0 ## If Script not found Exit ${if} $0 == -1 ## Script Dosen't Exist StrCpy $0 -1 Goto FunctionExit ${endif} ## Remove LGP Script ${registry::DeleteKey} "HKLM\$0" $2 ${if} $2 == -1 ## Script Dosen't Exist StrCpy $0 -1 Goto FunctionExit ${endif} ## Reindex Registry Keys ${LGPScript::_ReindexLGP} $1 $2 ## ReWrite Values to INI File ${LGPScript::_SaveToINI} $1 StrCpy $0 0 FunctionExit: ## Restore Registers ; Stack: $2 $1 $0 Pop $2 ; Stack: $1 $0 Pop $1 ; Stack: $0 Exch $0 ; Stack: <ReturnCode> FunctionEnd Function _LGPScript_Create ; (Mode, Command, Parameters) (ReturnCode) ClearErrors ## Preserve registers ; Stack: Parameters Command Mode Exch $0 ; Stack: $0 Command Mode Exch 2 ; Stack: Mode Command $0 Exch $1 ; Stack: $1 Command $0 Exch ; Stack: Command $1 $0 Exch $2 ; Stack: $2 $1 $0 Push $3 ; Stack: $3 $2 $1 $0 Push $4 ; Stack: $4 $3 $2 $1 $0 Push $5 ; Stack: $5 $4 $3 $2 $1 $0 Push $6 ; Stack: $6 $5 $4 $3 $2 $1 $0 ; $0 = Parameters ; $1 = Mode ; $2 = Command ## Verify Script not previously defined ${LGPScript::_Find} $1 $2 $0 $3 ${if} $3 != -1 ## Previously Defined BreakOut StrCpy $0 0 Goto FunctionExit ${endif} ## Get LGP Path ${LGPScript::_GetLGPRegPath} $1 $3 ## Get Next Available LGP index ${for} $4 0 1000 !warning "Current Local LGP index lookup is based on {registry::KeyExists} and dose not validate against invalid or empty definitions" ${registry::KeyExists} 'HKLM\$3\$4' $5 ${If} $5 == -1 StrCpy $3 "$3\$4" ${ExitFor} ${ElseIf} $4 == 1000 ## FAIL: No available index positions detected [Highly unlikly] ;StrCpy $3 "$3\0" StrCpy $0 -1 Goto FunctionExit ${EndIf} ${next} ## Write Script Definition into the Registry ${registry::Write} "HKLM\$3" "ExecTime" "0000000000000000" "REG_QWORD" $4 ${If} $4 == -1 StrCpy $0 -1 Goto FunctionExit ${EndIf} ${registry::Write} "HKLM\$3" "Parameters" `$0` "REG_SZ" $4 ${If} $4 == -1 StrCpy $0 -1 Goto FunctionExit ${EndIf} ${registry::Write} "HKLM\$3" "Script" `$2` "REG_SZ" $4 ${If} $4 == -1 StrCpy $0 -1 Goto FunctionExit ${EndIf} ## Reindex Registry Keys ${LGPScript::_ReindexLGP} $1 $2 ## Write Values to INI File ${LGPScript::_SaveToINI} $1 StrCpy $0 0 FunctionExit: ## Restore Registers ; Stack: $6 $5 $4 $3 $2 $1 $0 Pop $6 ; Stack: $5 $4 $3 $2 $1 $0 Pop $5 ; Stack: $4 $3 $2 $1 $0 Pop $4 ; Stack: $3 $2 $1 $0 Pop $3 ; Stack: $2 $1 $0 Pop $2 ; Stack: $1 $0 Pop $1 ; Stack: $0 Exch $0 ; Stack: <ReturnCode> FunctionEnd ### Internal Functions Function _LGPScript_ReindexLGP ;(Mode) (ReturnCode) ClearErrors ## Preserve registers ; Stack: <Mode> Exch $0 ; Stack: $0 Push $1 ; Stack: $1 $0 Push $2 ; Stack: $2 $1 $0 Push $3 ; Stack: $3 $2 $1 $0 Push $4 ; Stack: $4 $3 $2 $1 $0 Push $5 ; Stack: $5 $4 $3 $2 $1 $0 Push $6 ; Stack: $6 $4 $3 $2 $1 $0 ;$0 = Mode ## Get LGP Path ${LGPScript::_GetLGPRegPath} $0 $0 ## Init StrLen $1 "$0" StrCpy $2 -1 ${registry::Open} "HKLM\$0" "/K=0 /V=1 /S=0 /G=1 /T=REG_SZ /B=1 /N=Script" $3 ## Use search and counter to identify ordering ${do} IntOp $2 $2 + 1 ${registry::Find} "$3" $4 $5 $5 $6 ${if} $6 == "" ${ExitDo} ${endif} ## Determine the StringOffset of the Index Key StrLen $5 $4 IntOp $5 $1 - $5 IntOp $5 $5 + 1 ## Get Index Key StrCpy $6 $4 "" $5 ;DetailPrint "Counter=$2" ;DetailPrint "Index=$6" ## Test ${If} $6 == $2 ${Continue} ${EndIf} ;DetailPrint "Counter<>Index" ## Out Of Order ${registry::MoveKey} "HKLM\$4" "HKLM\$0\$2" $5 ;DetailPrint "Source=HKLM\$4" ;DetailPrint "Destin=HKLM\$0\$2" ;DetailPrint "Result=$5" ${If} $5 == -1 StrCpy $0 -1 Goto FunctionExit ${EndIf} ${loop} StrCpy $0 0 FunctionExit: ${registry::Close} "$3" ClearErrors ## Restore Registers ; Stack: $6 $5 $4 $3 $2 $1 $0 Pop $6 ; Stack: $5 $4 $3 $2 $1 $0 Pop $5 ; Stack: $4 $3 $2 $1 $0 Pop $4 ; Stack: $3 $2 $1 $0 Pop $3 ; Stack: $2 $1 $0 Pop $2 ; Stack: $1 $0 Pop $1 ; Stack: $0 Exch $0 ; Stack: <ReturnCode> FunctionEnd Function _LGPScript_SaveToINI ;(Mode) ClearErrors ## Preserve Registers ; Stack: <Mode> Exch $0 ; Stack: $0 Push $1 ; Stack: $1 $0 Push $2 ; Stack: $2 $1 $0 Push $3 ; Stack: $3 $2 $1 $0 Push $4 ; Stack: $4 $3 $2 $1 $0 Push $5 ; Stack: $5 $4 $3 $2 $1 $0 Push $R1 ; Stack: $R1 $5 $4 $3 $2 $1 $0 Push $R2 ; Stack: $R2 $R1 $5 $4 $3 $2 $1 $0 Push $R3 ; Stack: $R3 $R2 $R1 $5 $4 $3 $2 $1 $0 Push $R4 ; Stack: $R4 $R3 $R2 $R1 $5 $4 $3 $2 $1 $0 ;$0 = Mode ## Windows x64? ${If} ${RunningX64} ${DisableX64FSRedirection} ${EndIf} ## Delete INI Section for Rewrite DeleteINISec "${_LGPScript_INIFILE}" "$0" ## Windows x64? ${If} ${RunningX64} ${EnableX64FSRedirection} ${EndIf} ## Get LGP Path ${LGPScript::_GetLGPRegPath} $0 $1 ## Loop Registry and save values to INI StrLen $2 "$1" ${registry::Open} "HKLM\$1" "/K=0 /V=1 /S=0 /G=1 /T=REG_SZ /B=1 /N=Script" $3 ${Do} ${registry::Find} "$3" $R1 $R2 $R3 $R4 ${if} $R4 == "" ${ExitDo} ${endif} ## Determine the StringOffset of the Index Key StrLen $4 $R1 IntOp $4 $2 - $4 IntOp $4 $4 + 1 ## Get Index Key StrCpy $R4 $R1 "" $4 ## Windows x64? ${If} ${RunningX64} ${DisableX64FSRedirection} ${EndIf} WriteINIStr "${_LGPScript_INIFILE}" "$0" "$R4CmdLine" "$R3" ${registry::Read} "HKLM\$R1" "Parameters" $4 $5 WriteINIStr "${_LGPScript_INIFILE}" "$0" "$R4Parameters" "$4" ## Windows x64? ${If} ${RunningX64} ${EnableX64FSRedirection} ${EndIf} ${loop} ${registry::Close} "$3" ClearErrors ## Windows x64? ;${If} ${RunningX64} ; ${DisableX64FSRedirection} ;${EndIf} ## Restore Registers ; Stack: $R4 $R3 $R2 $R1 $5 $4 $3 $2 $1 $0 Pop $R4 ; Stack: $R3 $R2 $R1 $5 $4 $3 $2 $1 $0 Pop $R3 ; Stack: $R2 $R1 $5 $4 $3 $2 $1 $0 Pop $R2 ; Stack: $R1 $5 $4 $3 $2 $1 $0 Pop $R1 ; Stack: $5 $4 $3 $2 $1 $0 Pop $5 ; Stack: $4 $3 $2 $1 $0 Pop $4 ; Stack: $3 $2 $1 $0 Pop $3 ; Stack: $2 $1 $0 Pop $2 ; Stack: $1 $0 Pop $1 ; Stack: $0 Pop $0 ; Stack: FunctionEnd Function _LGPScript_Find ; (Mode, Command, Parameters) (RegPath) ClearErrors ## Preserve registers ; Stack: Parameters Command Mode Exch $0 ; Stack: $0 Command Mode Exch 2 ; Stack: Mode Command $0 Exch $1 ; Stack: $1 Command $0 Exch ; Stack: Command $1 $0 Exch $2 ; Stack: $2 $1 $0 Push $3 ; Stack: $3 $2 $1 $0 Push $4 ; Stack: $4 $3 $2 $1 $0 Push $5 ; Stack: $5 $4 $3 $2 $1 $0 Push $6 ; Stack: $6 $5 $4 $3 $2 $1 $0 Push $7 ; Stack: $7 $6 $5 $4 $3 $2 $1 $0 Push $8 ; Stack: $8 $7 $6 $5 $4 $3 $2 $1 $0 ; $0 = Parameters ; $1 = Mode ; $2 = Command ## Get LGP Path ${LGPScript::_GetLGPRegPath} $1 $3 ## Search ${Registry::Open} "HKLM\$3" "/K=0 /V=0 /S=1 /B=1 /N='$2'" $4 ${DoUntil} $4 == 0 ${Registry::Find} "$4" $5 $6 $7 $8 ${if} $5 == "" ${ExitDo} ${endif} ${if} $6 == "Script" ${registry::Read} "HKLM\$5" "Parameters" $6 $7 ${if} $6 == $0 StrCpy $0 $5 ${Registry::Close} $4 Goto FunctionExit ${endif} ${endif} ${Loop} ${Registry::Close} $4 ## Failed to find it StrCpy $0 -1 FunctionExit: ## Restore registers ; Stack: $8 $7 $6 $5 $4 $3 $2 $1 $0 Pop $8 ; Stack: $7 $6 $5 $4 $3 $2 $1 $0 Pop $7 ; Stack: $6 $5 $4 $3 $2 $1 $0 Pop $6 ; Stack: $5 $4 $3 $2 $1 $0 Pop $5 ; Stack: $4 $3 $2 $1 $0 Pop $4 ; Stack: $3 $2 $1 $0 Pop $3 ; Stack: $2 $1 $0 Pop $2 ; Stack: $1 $0 Pop $1 ; Stack: $0 Exch $0 ; Stack: <RegPath / RC> FunctionEnd Function _LGPScript_CreateLGP ; LGPRegPath (ReturnCode) ClearErrors ## Preserve registers ; Stack: <LGPRegPath> Exch $0 ; Stack: $0 Push $1 ; Stack: $1 $0 ## If we are running on a Windows x64 system we need to dispable file redirection to ensure we get the correct System32 Path ${If} ${RunningX64} ${DisableX64FSRedirection} ${EndIf} ## Write Standard Template to Registry ${registry::Write} "HKLM\$0" "FileSysPath" "$SYSDIR\GroupPolicy\Machine" "REG_SZ" $1 ${registry::Write} "HKLM\$0" "DisplayName" "Local Group Policy" "REG_SZ" $1 ${registry::Write} "HKLM\$0" "GPO-ID" "LocalGPO" "REG_SZ" $1 ${registry::Write} "HKLM\$0" "GPOName" "Local Group Policy" "REG_SZ" $1 ${registry::Write} "HKLM\$0" "SOM-ID" "Local" "REG_SZ" $1 ${If} ${RunningX64} ${EnableX64FSRedirection} ${EndIf} ## Check for Errors ${If} ${Errors} StrCpy $0 -1 ${else} StrCpy $0 0 ${EndIf} ## Restore registers ; Stack: $1 $0 Pop $1 ; Stack: $0 Exch $0 ; Stack: RetuenCode FunctionEnd Function _LGPScript_GetLGPRegPath ;(Mode) (RegPath) ClearErrors ## Preserve registers ; Stack: <Mode> Exch $0 ; Stack: $0 Push $1 ; Stack: $1 $0 Push $2 ; Stack: $2 $1 $0 Push $3 ; Stack: $3 $2 $1 $0 Push $4 ; Stack: $4 $3 $2 $1 $0 Push $5 ; Stack: $5 $4 $3 $2 $1 $0 ;$0 = Mode ## Windows x64 Check ;${If} ${RunningX64} ; SetRegView 64 ;${EndIf} ## Quickly Test to see if any Startup Scripts are defined ${registry::KeyExists} 'HKLM\${_LGPScript_REGPATH}\$0' $1 ${If} $1 == -1 ## It appears as the LocalGPO is not defined. StrCpy $0 '${_LGPScript_REGPATH}\$0\0' ${LGPScript::_CreateLGP} $0 $1 ${if} $1 == -1 StrCpy $0 -1 ${endif} Goto FunctionExit ${EndIf} ## Search for Local LGP ${Registry::Open} "HKLM\${_LGPScript_REGPATH}\$0" "/K=0 /V=0 /S=1 /B=1 /N='LocalGPO'" $1 ${DoUntil} $1 == 0 ${Registry::Find} "$1" $2 $3 $4 $5 ${if} $5 == "" ${ExitDo} ${endif} ${if} $3 == "GPO-ID" StrCpy $0 $2 ${Registry::Close} $1 Goto FunctionExit ${endif} ${Loop} ${Registry::Close} $1 ## No Local LGP detected, Looks like we need to create one. ## Locate next available LGP index ${for} $1 0 100 ${registry::KeyExists} 'HKLM\${_LGPScript_REGPATH}\$0\$1' $2 ${if} $2 == -1 StrCpy $0 '${_LGPScript_REGPATH}\$0\$1' ${LGPScript::_CreateLGP} $0 $1 Goto FunctionExit ${endif} ${next} ## Epic Fail if here StrCpy $0 -1 FunctionExit: ## Restore the registers ; Stack: $5 $4 $3 $2 $1 $0 Pop $5 ; Stack: $4 $3 $2 $1 $0 Pop $4 ; Stack: $3 $2 $1 $0 Pop $3 ; Stack: $2 $1 $0 Pop $2 ; Stack: $1 $0 Pop $1 ; Stack: $0 Exch $0 ; Stack: <RegPath> ;${If} ${RunningX64} ; SetRegView lastused ;${EndIf} FunctionEnd !endif # ___LGPSCRIPT__NSH___