SMS-like Software Distribution in the Background
Keywords: SMS-like software distribution in the background
I was just perusing the tech-support articles, and I noticed that someone wanted to know if Winbatch can accomplish software distribution to a workstation with no-one logged in. The article ID was W14614. I have written a script that can accomplish software distribution tasks when converted to run as a service. The compiled Winbatch executable must be run as a service using an administrative context (the service is configured to use a user that is a member of the local administrator group - similar to the SMS client installation service). The winbatch script/service can be configured in many ways, including how often to query the 3 possible sysupdate.ini files for updates. The script compares local registry keys (set via the Wise installation packages) to the contents of the .ini files. If versions are out of sync, the script will install the software, write status to a log file, and then fall asleep again until the next cycle (default is 5 minutes).The script is compiled as a .exe file, and then the srvany.exe/instsrv.exe tools (from the NT4 resource kit) are used to configure it as a service. Since it is a service, it is always running, even when there is nobody logged into the workstation.
The sysupdate.ini files contain parameters to ensure that distributions only occur at a particular date and beyond, or within a particular time window (such as midnight to 6:00 am).
I am including a sample sysupdate.ini file and the code. I have changed some of the values for registry keys, and have tried to make them generic. However, I have not recompiled this script with the new values so I do not know if it will work right out of the door.
Sysupdate.ini:
[Application List] WinZip7=1 Adobe3=1 [WinZip7] Version=20000203 Action=\Packages\Winzip\Winzip7.exe Year=2000 Month=5 Day=12 Starthour=20 Stophour=24 [Adobe3] Version=19990502 Action=\Packages\Adobe\Acroread.exe Year=2000 Month=7 Day=1 Starthour=2 Stophour=6Sysupdate.wbt:
;** Initialize Log file settings, and specify the drives that contain copies ;** of sysupdate.ini. The copy on the F: drive is global, whereas the copy on the ;** R: drive is site-specific. LogDate=TimeDate() LogFile="c:\winnt\sysupdate.log" Fupdfile="sysupdate.ini" Rupdfile="sysupdate.ini" ;** Determine how often the workstation queries the contents of all sysupdate.ini files ;** for updates. Default to 5 minutes if no value is specified. If RegExistKey(@REGMACHINE, "Software\SYSUpdate\SysDistFreq") regkey=RegOpenKey(@REGMACHINE, "Software\SYSUpdate") QueryTime=RegQueryValue(regkey, "SysDistFreq") RegCloseKey(regkey) Else QueryTime=300 Endif ;** Write first log message LogMessage=Strcat("%LogDate% - Initialized SYSUpdate service with %QueryTime% second query frequency.",@CRLF) GoSub WriteLog ;** Determine which server will act as the Distribution Server for packages ;** This value is set either via the registry or in the environment ErrorMode(@OFF) If RegExistValue(@REGMACHINE, "Software\SYSUpdate[DistServer]") regkey=RegOpenKey(@REGMACHINE, "Software\SYSUpdate") CurrentServer=RegQueryValue(regkey, "DistServer") RegCloseKey(regkey) Else regkey=RegOpenKey(@REGMACHINE, "SYSTEM\CurrentControlSet\Control\Session Manager\Environment") UNCCurrentServer=RegQueryExpSz(regkey,"[DistServer]") CurrentServer=StrSub(UNCCurrentServer,3,-1) RegCloseKey(regkey) Endif ErrorMode(@CANCEL) If CurrentServer=="" LogMessage=Strcat("%LogDate% - SYSUpdate Init failure - DistServer value not set.",@CRLF) Gosub WriteLog Exit Endif while 1 ;** Read and itemize the %DistServer%\Login\sysupdate.ini file, and execute updates if necessary ;** One of the 2 sysupdate.ini files (F:\Login\sysupdate.ini) is identical on all distribution ;** servers in the platform, ensuring that the update is processed by all workstations. It can be ;** replicated to all distribution servers ensuring only 1 copy will need to be updated (the master copy). ;** The second copy is specific to the server, ensuring that only the workstations that use this particular ;** server will get the distribution. The third copy resides on the workstation's C: drive, and can ;** be used for individual installation of packages. INIFile=StrCat("\\","%CurrentServer%","\Login\","%Fupdfile%") GoSub ParseIniFile INIFile=StrCat("\\","%CurrentServer%","\Regional\","%Rupdfile%") GoSub ParseIniFile INIFile="c:\winnt\sysupdate.ini" GoSub ParseIniFile TimeDelay(QueryTime) EndWhile :ParseIniFile FE=FileExist(INIFile) If FE==@False Then Goto NextLoop LogDate=TimeDate() List=IniItemizePvt("Application List", INIFile) Count=ItemCount(List, @TAB) ;** Parse registry and compare against appropriate SYSUPDATE.INI file For X=1 to Count App=ItemExtract(x, List, @TAB) AppVer=IniReadPvt(App, "Version","None",INIFile) AppAction=IniReadPvt(App, "Action","None",INIFile) AppYear=IniReadPvt(App, "Year", "None", INIFile) AppMonth=IniReadPvt(App, "Month","None",INIFile) AppDay=IniReadPvt(App, "Day", "None", INIFile) AppStartHour=IniReadPvt(App, "Starthour", "None", INIFile) AppStopHour=IniReadPvt(App, "Stophour", "None", INIFile) If AppVer=="None" Then Goto AppVerMissing ErrorMode(@OFF) regkey=RegOpenKey(@REGMACHINE, "Software\SYSUpdate") CurrVer=RegQueryValue(regkey, "%App%") RegCloseKey(regkey) ErrorMode(@CANCEL) If CurrVer < AppVer Then Goto AppUpdateEXE Goto AppUpdateComplete :AppVerMissing LogMessage=Strcat("%LogDate% - %App%: %INIFile% file error.",@CRLF) GoSub WriteLog Goto AppUpdateComplete :AppUpdateEXE ;** Set variables for current time information, and apply logic for year 1999 or 2000+. CurrTime=TimeYmdHms() YearPos=StrScan(CurrTime,":",1,@FWDSCAN) If YearPos==5 CurrYear=StrSub(CurrTime,1,4) CurrMonth=StrSub(CurrTime,6,2) CurrDay=StrSub(CurrTime,9,2) CurrHour=StrSub(CurrTime,12,2) CurrMin=StrSub(CurrTime, 15,2) Else CurrYear=StrSub(CurrTime,1,2) If CurrYear==99 CurrYear=1999 Else CurrentYear=2000+%CurrYear% CurrYear=CurrentYear Endif CurrMonth=StrSub(CurrTime,4,2) CurrDay=StrSub(CurrTime,7,2) CurrHour=StrSub(CurrTime,10,2) CurrMin=StrSub(CurrTime, 13,2) Endif ;** Determine if we are beyond the date specified in SYSUpdate.ini, and if so, run the update If AppYear!="None" If CurrYear>AppYear GoSub InstallSysUpdate Else If CurrYear==AppYear If CurrMonth>AppMonth GoSub InstallSysUpdate Else If CurrMonth==AppMonth If CurrDay>=AppDay GoSub InstallSysUpdate Endif Endif Endif Endif Endif Else GoSub InstallSysUpdate Endif :AppUpdateComplete Next :NextLoop Return :InstallSysUpdate UpdateToRun=StrCat("\\","%CurrentServer%","%AppAction%") ;** Determine if the workstation is in the appropriate distribution window, ;** and execute the package if this is the case. If AppStartHour!="None" If AppStartHour=AppStartHour If CurrHour<=AppStopHour LogDate=TimeDate() LogMessage=Strcat("%LogDate% - %App%: Started system update...",@CRLF) GoSub WriteLog RunWait(UpdateToRun,"") LogDate=TimeDate() LogMessage=Strcat("%LogDate% - %App%: System update complete.",@CRLF) GoSub WriteLog Endif Endif Else If CurrHour>=AppStartHour LogDate=TimeDate() LogMessage=Strcat("%LogDate% - %App%: Started system update...",@CRLF) GoSub WriteLog RunWait(UpdateToRun,"") LogDate=TimeDate() LogMessage=Strcat("%LogDate% - %App%: System update complete.",@CRLF) GoSub WriteLog Else If CurrHour<=AppStopHour LogDate=TimeDate() LogMessage=Strcat("%LogDate% - %App%: Started system update...",@CRLF) GoSub WriteLog RunWait(UpdateToRun,"") LogDate=TimeDate() LogMessage=Strcat("%LogDate% - %App%: System update complete.",@CRLF) GoSub WriteLog Endif Endif Endif Else LogDate=TimeDate() LogMessage=Strcat("%LogDate% - %App%: Started system update...",@CRLF) GoSub WriteLog RunWait(UpdateToRun,"") LogDate=TimeDate() LogMessage=Strcat("%LogDate% - %App%: System update complete.",@CRLF) GoSub WriteLog Endif Return :WriteLog FE=FileExist(LogFile) If FE==@False Then handle=FileOpen(LogFile, "WRITE") FileWrite(handle, "** SYSUpdate Log File **") FileClose(handle) EndIf fs1=FileSize(LogFile) binbuf1=BinaryAlloc(fs1 + 100) BinaryRead(binbuf1, LogFile) a=BinaryEodGet(binbuf1) BinaryPokeStr(binbuf1, a, LogMessage) BinaryWrite(binbuf1, LogFile) BinaryFree(binbuf1) Return
Article ID: W14961