Table of Contents
List of Figures
List of Tables
The Copyright of this manual holds the uib gmbh in Mainz, Germany.
This manual is published under the creative commons license
Attribution - ShareAlike (by-sa).

A German description you will find here:
http://creativecommons.org/licenses/by-sa/3.0/de/
The German legally binding license:
http://creativecommons.org/licenses/by-sa/3.0/de/legalcode
The English description:
http://creativecommons.org/licenses/by-sa/3.0/
The English license: http://creativecommons.org/licenses/by-sa/3.0/legalcode
The opsi software is in most parts open source.
Not open source are only this new parts which are still under cofunding.
see:
http://uib.de/en/opsi_cofunding/index.html
All the rest of the source code is published under the GPLv3:

The legally binding GPLv3 license:
http://www.gnu.org/licenses/gpl.html
The name opsi is a registered trade mark of the uib gmbh.
The opsi-logo is owned by the uib gmbh and may be used only with explicit permission.
The open source program opsi-winst serves in the context of opsi – open pc server integration (cf. www.opsi.org) – as the central function for initiating and performing the automatic software installation. It may also be used stand alone as a tool for setup programs for any piece of software.
opsi-winst is basically an interpreter for a specific, rather simple script language which can be used to express all relevant elements of a software installation.
A software installation that is described by a opsi-winst script and performed by executing the script has several advantages compared with installations that are managed by a bunch of shell commands (e. g. copy etc.):
There are different modi for writing to the Windows registry:
opsi-winst can be started with different sets of parameters depending on context and purpose of use.
There are the following syntactical schemata:
(1) Show usage:
opsi-winst /?
opsi-winst /h[elp]
(2 ) Execute a script
opsi-winst <script file>
[/logfile <log file> ]
[/batch | /histolist <opsi-winst config file path>]
[/usercontext <[domain\]user name> ]
[/parameter parameter string]
(3) Execute a list of scripts (separated by semicolons) one by one:
opsi-winst /scriptfile <scriptfile> [;<script file>]* [ /logfile <log file> ]
[/batch | /silent ]
[/usercontext <[domain\]user name> ]
[/parameter <parameter string>]
4) Read the PC configuration from the opsi service and act accordingly, since opsi-winst 4.3
opsi-winst /opsiservice <opsiserviceurl>
[/clientid <clientname>]
[/username <username>]
[/password <password>]
[/sessionid <sessionid>]
[/logfile <logfile>]
[/allloginscripts]
[/silent]
[/parameter <parameterstring>]
Some explanations:
c:\tmp\instlog.txt
/parameter, is accessible for every called opsi-winst script (via the string function ParamStr).
Explanations to (2) and (3) :
/batch is used, then opsi-winst shows only its "batch surface" offering no user dialogs. By option /silent event the batch surface is suppressed. Without using option /batch we get into the interactive mode where script file and log file can be chosen interactively (mainly for testing purposes).
winstconfigfilepath parameter which is designated by /histofile refers to a file in ini file format that holds the (in interactive mode) last used script file names. The dialogue surface presents a list box that presents these file names for choosing the next file to interpret. If winstconfigfilepath ends with "\" it is assumed to be a directory name and WINST.INI serves as file name.
Explanations to (4):
clientid is the full qualified computer name
/allloginscripts opsi-winst can do configurations for the logged in user (particularly in a Roaming Profile context). This is a co-funding feature - you need to buy it in order to use it./silent event the batch surface is suppressed.
The not interactive mode is implied.
By default log files are written into the directory c:\tmp which opsi-winst tries to create. If opsi-winst has no access to this directory it uses the user-TEMP directory.
The default log file name is instlog.txt. The log file name and location will be overwritten via the specific command line option.
In the case, that opsi-winst executes a script in /batch mode and with a specified (and working) usercontext, the default logging path is the opsi/tmp in the appdata directory of the user. This will be overwritten by an explicit given log path.
In addition, opsi-winst uses the logging directory for saving certain temporary files.
If wanted, opsi-winst writes the error data to a second file on a network drive or sends them to a syslog demon.
The feature can be configured in the Windows registry:
In HKEY_LOCAL_MACHINE, we have in a standard installation the key \SOFTWARE\opsi.org. We can create a subkey syslogd with a variable remoteerrorlogging. Its value determines if and, if yes, by which method a central logging shall take place.
Furthermore, in
HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\syslogd
we have to observe three up to three variables:
The following table shows the possible values for the facility:
ID_SYSLOG_FACILITY_KERNEL = 0; // kernel messages ID_SYSLOG_FACILITY_USER = 1; // user-level messages ID_SYSLOG_FACILITY_MAIL = 2; // mail system ID_SYSLOG_FACILITY_SYS_DAEMON = 3; // system daemons ID_SYSLOG_FACILITY_SECURITY1 = 4; // security/authorization messages (1) ID_SYSLOG_FACILITY_INTERNAL = 5; // messages generated internally by syslogd ID_SYSLOG_FACILITY_LPR = 6; // line printer subsystem ID_SYSLOG_FACILITY_NNTP = 7; // network news subsystem ID_SYSLOG_FACILITY_UUCP = 8; // UUCP subsystem ID_SYSLOG_FACILITY_CLOCK1 = 9; // clock daemon (1) ID_SYSLOG_FACILITY_SECURITY2 = 10; // security/authorization messages (2) ID_SYSLOG_FACILITY_FTP = 11; // FTP daemon ID_SYSLOG_FACILITY_NTP = 12; // NTP subsystem ID_SYSLOG_FACILITY_AUDIT = 13; // log audit ID_SYSLOG_FACILITY_ALERT = 14; // log alert ID_SYSLOG_FACILITY_CLOCK2 = 15; // clock daemon (2) ID_SYSLOG_FACILITY_LOCAL0 = 16; // local use 0 (local0) ID_SYSLOG_FACILITY_LOCAL1 = 17; // local use 1 (local1) ID_SYSLOG_FACILITY_LOCAL2 = 18; // local use 2 (local2) ID_SYSLOG_FACILITY_LOCAL3 = 19; // local use 3 (local3) ID_SYSLOG_FACILITY_LOCAL4 = 20; // local use 4 (local4) ID_SYSLOG_FACILITY_LOCAL5 = 21; // local use 5 (local5) ID_SYSLOG_FACILITY_LOCAL6 = 22; // local use 6 (local6) ID_SYSLOG_FACILITY_LOCAL7 = 23; // local use 7 (local7)
On principle: opsi-winst is an interpreter for a specific, easy to use scripting language which is tailored for the requirements of software installations. A script should be an integrated description, and a means of control, for the installation of one piece of software.
The following section sketches the structure of a opsi-winst script. The purpose is to identify the book marks of a script: in which way we to have to look into it to understand its processing.
All elements shall be described more in detail in the further section. The purpose then will be to show how scripts can be modified or developed.
opsi-winst scripts are roughly derived from .INI files. They are composed of sections, which are marked by a title (the section name) which is written in brackets [].
Schematically a opsi-winst script looks like this one (here with a check which operating system is installed):
[Actions]
Message "Installation of Mozilla"
SetLogLevel=6
;which Windows-Version?
DefVar $MSVersion$
Set $MSVersion$ = GetMsVersionInfo
if ($MSVersion$>="6")
sub_install_win7
else
if ( $MSVersion$ = "5.1" )
sub_install_winXP
else
stop "not a supported OS-Version"
endif
endif
[sub_install_win7]
Files_copy_win7
WinBatch_Setup
[sub_install_winXP]
Files_copy_XP
WinBatch_SetupXP
[Files_copy_win7]
copy "%scriptpath%\files_win7\*.*" "c:\temp\installation"
[Files_copy_winxp]
copy "%scriptpath%\files_winxp\*.*" "c:\temp\installation"
[WinBatch_Setup]
c:\temp\installation\setup.exe
[WinBatch_SetupXP]
c:\temp\installation\install.exeHow can we read the sections of this script?
The script as a whole serves as a program, an instruction for an installation process. Therefore each of its sections can be seen as a a subprogram (or "procedure" or "method"). The script is a collection of subprograms.
The human reader as well as an interpreting software has to know at which element in this collection reading must start.
Execution of a opsi-winst script begins with working on the sections[Initial] and [Actions] (in this order). All other sections are called as subroutines from these two sections. This process is only recursive for Sub sections: Sub sections have the same syntax as Initial and Actions sections and may contain calls for further subroutines.
If a script is run as userLoginScript and it contains a section [ProfileActions], so the script interpretation will be started at the ProfleActions section.
This gives reason to make the distinction between primary and secondary subprograms:
The primary or general control sections comprise
The procedural logic of the script is determined by the sequence of calls in these sections.
The secondary or specific sections can be called from any primary section but have a different syntax. The syntax is derived from the functional requirements and library conditions and conventions for the specific purposes. Therefore no further section can be called from a secondary section.
At this moment there are the following types of secondary sections:
Meaning and syntax of the different section types are treated in Chapter 7, Syntax and Meaning of Primary Sections of a opsi-winst Script and Chapter 8, Secondary Sections.
Textual values (string values) in the primary sections can be given in different ways:
EnvVar ("Username")GetMsVersionInfo(More on this in Section 7.3, “String Expressions, String Values, and String Functions”)
There is no analogous way of using string expressions in the secondary sections. They follow there domain specific syntax. e.g. for copying commands similar to the windows command line copy command. Up to this moment it is no escape syntax implemented for transporting primary section logic into secondary sections.
The only way to transport string values into secondary sections is the use of the names of variables and constants as value container in these sections. Lets have a closer look at the variables and constants of a opsi-winst script:
In a opsi-winst script, variables and constants appear as "words", that are interpreted by opsi-winst and "contain" values. "Words" are sequences of characters consisting of letters, numbers and some special characters (in particular ".", "-", "_", "$", "%"), but not blanks, but no brackets, parentheses, or operator signs ("+") .
opsi-winst variables and constants are not case-sensitive.
There exist the following types of variables or constants:
Global text constants, shortly constants,
contain values which are present by the opsi-winst program and cannot be changed in a script. Before interpreting the script opsi-winst replaces each occurrence of the pure constant name with its value in the whole script (textual substitution).
An example will make this clear:
The constant %ScriptPath% is the predefined name of the location where opsi-winst found and read the script that it just executes. This location may be, e.g., p:\product. Then we have to write
"%ScriptPath%"
in the script when we want do get the value
"p:\product".
DefVar statement before they can be used. In primary sections, values can be assigned to variables (once ore more times). They can be used as elements in composed expressions (like addition of strings) or as function arguments.DefStringList statement. In primary sections they can be used for many purposes, e.g. collecting strings, manipulating strings, building sections.
In detail:
Scripts shall work in a different contexts without manual changes. The contexts can be characterized by system values as OS version or certain paths. opsi-winst introduces such values as constants into the script.
The fundamental characteristics of a text constant is the way how the values which it represents come intro the script interpretation process:
The name of the constant, that is the pure sequences of chars, is substituted by its fixed value in the whole script before starting the script execution.
The replacement does not take into account any syntactical context in which the name possibly occur (exactly like with variables in secondary sections).
opsi-winst implements constants %ScriptPath% for the location of the momentarily interpreted script and %System% for the name of the windows system directory. The following (Files) subsection defines a command that copies all files from the script directory to the windows system directory:
[files_do_my_copying] copy "%ScriptPath%\system\*.*" "%System%"
At this moment the following constants are implemented:
%ProgramFilesDir%: c:\program files
%ProgramFiles32Dir%: c:\Program Files (x86)
%ProgramFiles64Dir%: c:\program files
%ProgramFilesSysnativeDir% : c:\program files
%Systemroot% : c:\windows
%System% : c:\windows\system32
%Systemdrive% : c:
%ProfileDir% : c:\Documents and Settings
%AllUsersProfileDir% or %CommonProfileDir% : c:\Documents and Settings\All Users
%CommonStartMenuPath% or %CommonStartmenuDir% : c:\Documents and Settings\All Users\Startmenu
%CommonAppdataDir% : c:\Documents and Settings\All Users\Application Data
%CommonDesktopDir%
%CommonStartupDir%
%CommonProgramsDir%
%AppdataDir% or %CurrentAppdataDir% : c:\Documents and Settings\%USERNAME%\Application Data
%CurrentStartmenuDir%
%CurrentDesktopDir%
%CurrentStartupDir%
%CurrentProgramsDir%
%CurrentSendToDir%
%CurrentProfileDir% //since 4.11.2.1
In Files sections that are called with option /AllNtUserProfiles there is a pseudo variable
%UserProfileDir%
When the section is executed for each user that exists on a work station this variable represents the name of the profile directory of the user just treated.
%CurrentProfileDir% // since 4.11.2.1
may be used instead of the older %UserProfileDir% in order to have Files-sections which may be used also for userLoginScripts.
%ScriptPath% or %ScriptDir% : represents the path of the current opsi-winst script (without closing backslash). Using this variable we can build path and file names in scripts that are relative to the location of the script. So, everything can be copied, called from the new place, and all works as before.
%ScriptDrive% : he drive where the just executed opsi-winst script is located (including the colon).
%WinstDir% : The location (without closing backslash) of the running opsi-winst.
%WinstVersion% : Version string of the running winst.
%Logfile% : The name of the logfile which opsi-winst is using.
Example:
The code:
comment "Testing: "
message "Testing constants: "+"%"+"winstversion" +"%"
set $ConstTest$ = "%WinstVersion%"
set $InterestingFile$ = "%winstdir%\winst.exe"
if not (FileExists($InterestingFile$))
set $InterestingFile$ = "%winstdir%\winst32.exe"
endif
set $INST_Resultlist$ = getFileInfoMap($InterestingFile$)
set $CompValue$ = getValue("file version with dots", $INST_Resultlist$ )
if ($ConstTest$ = $CompValue$)
comment "passed"
else
set $TestResult$ = "not o.k."
LogWarning "failed"
endifresults to the following log:
comment: Testing:
message Testing constants: %winstversion%
Set $ConstTest$ = "4.10.8.3"
The value of the variable "$ConstTest$" is now: "4.10.8.3"
Set $InterestingFile$ = "N:\develop\delphi\winst32\trunk\winst.exe"
The value of the variable "$InterestingFile$" is now: "N:\develop\delphi\winst32\trunk\winst.exe"
If
Starting query if file exist ...
FileExists($InterestingFile$) <<< result true
not (FileExists($InterestingFile$)) <<< result false
Then
EndIf
Set $INST_Resultlist$ = getFileInfoMap($InterestingFile$)
retrieving strings from getFileInfoMap [switch to loglevel 7 for debugging]
Set $CompValue$ = getValue("file version with dots", $INST_Resultlist$ )
retrieving strings from $INST_Resultlist$ [switch to loglevel 7 for debugging]
The value of the variable "$CompValue$" is now: "4.10.8.3"
If
$ConstTest$ = $CompValue$ <<< result true
($ConstTest$ = $CompValue$) <<< result true
Then
comment: passed
Else
EndIf%Host% : (Deprecated) The value of a environmental variable host (traditionally meaning the opsi server name, not to confuse with %HostID% (meaning the client network name).
%PCName%: The value of the environmental variable PCName, when existing. Otherwise the value of the environmental variable computername. (Should be the netbios name of the PC)
%IPName% : The dns name of the pc. Usually identical with the netbios name and therefore with %PCName% besides that the netbios names uses to be uppercase.
%Username% : Name of the logged in user.
%HostID% : Should be the fully qualified domain name of the opsi client as it is supplied from the command line or otherwise.
%opsiserviceURL% : The (usually https://) URL of the opsi service.
%opsiServer% : The server name derived from the %opsiserviceURL%.
%opsiserviceUser% : The user ID for which there is a connection to the opsi service.
%opsiservicePassword% : The user password used for the connection to the opsi service. The password is eliminated when logging by the standard opsi-winst logging functions.
%installingProdName%: The productid of the product that is actually installed via call by the opsi-service. Empty if the Script ist not started by the opsi-service.
%installingProdVersion%: A String combinated from <productversion>-<packageversion> for the product that is actually installed via call by the opsi-service. Empty if the Script ist not started by the opsi-service.
%installingProduct% : (Deprecated) The name (productId) of the product for which the service has called the running script. In case that there the script is not run via the service the String is empty.
String variables must be declared before they can be used. The syntax for the declaration reads
DefVar <variable name>
e.g.
DefVar $MsVersion$
Explanation:
Recommendation:
$
As it is appropriate for a variable, it can take on one value resp. a series of values while a script is progressing. The values are assigned by statements with syntax
Set <Variablenname> = <Value>
<Value> means any (String valued) expression.
Examples (For Examples see Section 7.3, “String Expressions, String Values, and String Functions”):
Set $OS$ = GetOS Set $NTVersion$ = "nicht bestimmt" if $OS$ = "Windows_NT" Set $NTVersion$ = GetNTVersion endif DefVar $Home$ Set $Home$ = "n:\home\user name" DefVar $MailLocation$ Set $MailLocation$ = $Home$ + "\mail"
In primary sections of a opsi-winst script, a variable "holds" a value. When it is declared it is initialized with the empty String "". When a new value is assigned to it via the set command, it represents this value.
In a primary section a variable can replace any String expression resp. can be a component of a String expression, e.g.
Set $MailLocation$ = $Home$ + "\mail"
In a primary section the variable name denotes an object that represents a string, If we add the variable we mean that the underlying string shall be added somehow.
This representational chain is shortcut in a secondary section. Just the variable name now stands for the string.
When a secondary section is loaded and opsi-winst starts its interpretation the sequence of chars of a variable name is directly replaced by the value of the variable.
Example:
A copy command in a files section shall copy a file to
"n:\home\user name\mail\backup"
kopiert werden.
We first set $MailLocation$ to the directory above it:
DefVar $Home$ DevVar $MailLocation$ Set $Home$ = "n:\home\user name" Set $MailLocation$ = $Home$ + "\mail"
$MailLocation$ is now holding
"n:\home\user name\mail"
In a primary section we may now express the directory
"n:\home\user name\mail\backup"
by
$MailLocation$ + "\backup"
The same directory has to be designated in a secondary section as:
"$MailLocation$\backup"
A fundamental difference between the thinking of variables in primary vs. secondary sections is that, in a primary section, we can form an assignment expression like
$MailLocation$ = $MailLocation$ + "\backup"
As usual, this means that $MailLocation$ first has some initial value and takes on a new value by adding some string to the initial value. The reference from the variable is dynamic, and may have a history.
In a secondary section any such expression would be worthless (and eventually wrong), since $MailLocation$ is bound to be replaced by some fixed string (at all occurrences virtually in the same moment).
Variables for string lists must be declared in a DefStringList statement, e.g.
DefStringList SMBMounts
A string list can serve e.g. as container for the captured output of a shell program. The collected strings can be manipulated in a lot of ways. In detail this will be treated in the section on string list processing (see Section 7.4, “String List Functions and String List Processing”).
Wenn (geschachtelte) Sub-Sektionen in externe Dateien ausgelagert werden, müssen die aufgerufenen Sekundären Sektionen üblicherweise in der Datei untergebracht werden, aus der sie aufgerufen werden. Je nach verwendeter Komplexität des Syntax müssen sie evtl. zusätzlich auch in der Hauptdatei untergebracht werden.
As shortly presented in chapter 4 the Actions section of a script can be regarded as a the main method of the opsi-winst script and describes the global processing sequence. It may call subroutines - the Sub sections which may then recursively call Sub sections themselves.
The following sections explain syntax and use of the primary sections of a opsi-winst script.
There are possibly three kinds of primary sections in a script
Initial section (may be omitted),
Action section,
Sub sections
ProfileActions section
Initial and Action section are syntactically equivalent (but Initial has to keep the first place). By convention, in the Initial section some parametrizations of the script execution (e.g. the loglevel) are made. The Action section can be regarded as the main program in a opsi-winst script. It contains the sequence of actions that are controlled by the script.
Sub sections are as well syntactically equivalent. But they are a called from the Action section. Then, they can call themselves Sub sections.
A Sub section is determined by creating a name that begins with "Sub", e.g. Sub_InstallBrowser. By writing its name in the Action section we produce a call to the Sub section. The meaning of this call is defined by the content of the section in the script that begins with the bracketed name, in the example [Sub_InstallBrowser]
Subsections of second and higher order cannot host internal sections. Instead, their procedure calls must refer to sections defined in the main script file or defined as external sections (cf. Section 7.10, “Subprogram Calls”).
If (nested) sub sections are externalized to external files, the called sections has to be in that file where they are called from. According to the complexity of the script they may sometimes have to be placed also in the main file.
A ProfileActions section at a normal installation script may be used as a sub section with a special syntax. In a userLoginScript this section will be used as script start (instead of Actions). See chapter User Profile Management at the opsi-manual and Section 7.8, “Commands for userLoginScripts / Roaming Profile Support”.
Typical entries of an Initial section set some the opsi-winst execution attributes. The following example shows how error responses may be configured:
[Initial] SetLogLevel=5 ExitOnError=false ScriptErrorMessages=on TraceMode=off
This means that:
The above values are the default values, opsi-winst will assume them if these statements are missing.
To the details of syntax and meaning:
The old function LogLevel= is deprecated since opsi-winst version 4.10.3. For backward compatibility reasons Loglevels ste by this old function will be increased by 4 before they are used.
There are two syntactical variants for specifying the logging level:
SetLogLevel = <number> SetLogLevel = <String expression> I.e. the number can be given as an integer value or as a string expression (cf. section 6.3). In the second case, opsi-winst tries to evaluate the string expression as a number. There exist ten levels from 0 up to 9.
Es gibt zwei ähnliche Varianten, um den Loglevel zu spezifizieren:
SetLogLevel = <number>
SetLogLevel = <String expression>
I.e. the number can be given as an integer value or as a string expression (cf. Section 7.3, “String Expressions, String Values, and String Functions”). In the second case, opsi-winst tries to evaluate the string expression as a number.
There exist ten levels from 0 up to 9.
The statement
requiredWinstVersion <RELATION SYMBOL> <NUMBER STRING>
e.g.
requiredWinstVersion >= "4.3"
makes opsi-winst check if the desired version state is given. Otherwise an error message windows pops up.
This feature exists since opsi-winst version 4.3. For an earlier version, the statement is unknown, and the statement itsself is a syntactical error which will be indicated by syntax error window (cf. the following section). Therefore the statement can be used independently of the currently used opsi-winst version as long as the required version is at least version 4.3.
There are two kinds of errors which are treated in different ways:
In principal, syntactical errors are indicated by a pop up window for immediate correction, execution errors are logged in a log file to be analysed later.
The behaviour of opsi-winst when it recognizes a syntactical error is defined by the configuration statement
ScriptErrorMessages = <boolean value>There two configuration options for execution errors.
ExitOnError = <boolean value>TraceMode = <boolean value>StayOnTop = <Wahrheitswert>
With StayOnTop = true (or = on) we request, that - in batch mode - the opsi-winst window be on top on the windows which share the screen. That means it should be visible in the "foreground" as long as no other window having the same status wins.
According to the system manual the value cannot be changed while the program is running. But it seems that we can give a new value to it once.
StayOnTop has default false in order to avoid that some other process raises an error message which eventually can not be seen if opsi-winst keeps staying on top.
A String expression can be
An elementary String value is any sequence of characters that is enclosed in double or single citations marks, formally:
"<sequence of characters>"
or
'<sequence of characters>'
Example:
DefVar $ExampleString$ Set $ExampleString$ = "my Text"
If the sequence of chars itself contains citation marks we have to use the other kind of citation marks to enclose it:
DefVar $citation$ Set $citation$ = 'he said "Yes"'
If the sequence of chars is containing both kinds of citation marks we must use the following special expression:
EscapeString: <sequence of characters>
E.g. we can write:
DefVar $Meta_citation$ Set $Meta_citation$ = EscapeString: Set $citation$ = 'he said "Yes"'
Then the variable $Meta_citation$ will exactly contain the complete sequence of chars that follows the colon after "EscapeString" (including the blank). Such, $Meta_citation$ will contain the complete statement: Set $citation$ = 'he said "Yes"'
String concatenation is written using the addition sign ("+")
<String expression> + <String expression>
Example:
DefVar $String1$ DefVar $String2$ DefVar $String3$ DefVar $String4$ Set $String1$ = "my text" Set $String2$ = "and" Set $String3$ = "your text" Set $String4$ = $String1$ + " " + $String2$ + " " + $String3$
$String4$ then has value "my text and your text".
A String variable in a primary section "contains" a String value. In an String expression, it can always substitute an elementary string. For how to define and set String variables cf. Section 6.3, “String (or Text) Variables”.
The following sections present the variety of string functions.
GetOSGetMsVersionInfo.GetMsVersionInfo.Table 7.1. Windows Versions
| GetMsVersionInfo | Windows Version |
|---|---|
5.0 | Windows 2000 |
5.1 | Windows XP (Home, Prof) |
5.2 | XP 64 Bit, 2003, Home Server, 2003 R2 |
6.0 | Vista, 2008 |
6.1 | Windows 7, 2008 R2 |
see also GetMsVersionMap
GetSystemTypeThe function reads and returns the momentary value of a system environment variable.
E.g., we can retrieve which user is logged in by EnvVar ("Username"). ParamStr The function passes the the parameter string of the opsi-winst command line i.e. the command line parameter which is indicated by /parameter. If there is no such parameter ParamStr returns the empty string. GetLastExitCode returns the exit code (also called ErroLevel) of the last Winbatch call. GetUserSID(<Windows Username>) returns the SID for a given user (possibly with domain prefix in the form DOMAIN\USER).
EnvVar ( <string>)EnvVar ("Username").
ParamStrGetLastExitCodeGetUserSID(<Windows Username>)GetUsercontext/usercontext. IF this parameter was not userd the returned string is empty.
GetRegistryStringValue (<string>)KEY, and, in case it succeeds, to read and return the String value that belongs to the registry variable name X .
E.g.
GetRegistryStringValue ("[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] Shell")usually yields "Explorer.exe", the default Windows shell program.
If there is no registry key KEY or the variable X does not exist the function produces a warning message in the log file and returns the empty string.
For example:
If we made a standard entry with the value standard entry at the key HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\opsi-winst-test\test-4.0, we will get with
Set $CompValue$ = GetRegistryStringValue32 ("[HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\opsi-winst-test\test-4.0]")the following log:
Registry started with redirection (32 Bit) Registry key [HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\opsi-winst-test\test-4.0] opened Key closed The value of the variable "$CompValue$" is now: "standard entry"
GetRegistryStringValue32(<string>) → see Chapter 64 Bit
GetRegistryStringValue64(<string>) → see Chapter 64 Bit
GetRegistryStringValueSysNative(<string>) → see Chapter 64 Bit
RegString(<string>)RegString ("c:\windows\system\")yields
"c:\\windows\\system\\"
For historical reasons, there are three functions for reading values from configuration files which have ini file format. Since opsi 3.0 the specific product properties are retrieved from the opsi configuration demon (that may fetch it from a configuration file or from any other backend data container).
In detail:
Ini file format means that the file is a text file and is composed of "sections" each containing key value pairs:
[section1] Varname1=Value1 Varname2=Value2 ... [section2] ...
The most general function reads the value belonging to some key in some section of some ini file. Any parameter can be given as an arbitrary String expression:
GetValueFromInifile (<FILE>, <SECTION>, <KEY>, <DEFAULTVALUE>)FILE, retrieve the requested SECTION and find the value belonging to the specified KEY which the function will return. If any of these operations fail DEFAULTVALUE is returned.
The second function borrows its syntax from the ini file format itself, and may sometimes be easier to use. But since this syntax turns complicated in more general circumstances it is deprecated. The syntax reads:
GetIni ( <Stringausdruck> [ <character sequence> ] <character sequence> )GetProductProperty ( <PropertyName>, <DefaultValue>)<DefaultValue> will be returned.
The product properties can be used to configure variants of an installation.
E.g. the opsi UltraVNC network viewer installation may be configured using the options
The installation script branches according to the chosen values for these options which can be retrieved by
GetProductProperty("viewer", "yes")
GetProductProperty("policy", "factory_default")IniVar(<PropertyName>)GetHostsName(<string>)GetHostsAddr(<string>)ExtractFilePath(<string>)StringSplit (`STRINGWERT1, STRINGWERT2, INDEX)`splitString / takestring)
takeString(<index>,<list>)splitstring:
takeString(<index>, splitString(<string1>, <string2>)Example:
takeString(3, splitString ("\\server\share\directory", "\"))returns "share",
the given string slpitted at "\" returns the string list:
Index 0 - "" (empty string), because there is nothing before the first "\"
Index 1 - "" (empty string), because there is nothing before the second "\"
Index 2 - "server"
Index 3 - "share"
Index 4 - "directory"
takestring counts downward, if the index is negative, starting with the number of elements. Therefore,
takestring(-1, $list1$)
denotes the last element of String list $list1$.
SubstringBefore(<string1>, <string2>)splitString / takestring)
yields the sequence of characters of stringValue1 up to the beginning of stringValue2.SubstringBefore ("C:\programme\staroffice\program\soffice.exe", "\program\soffice.exe")returns "C:\programme\staroffice".
takeFirstStringContaining(<list>,<search string>)Trim(<string>)lower(<string>)unquote(<string>,<quote-string>) //since 4.11.2.1HexStrToDecStr(<string>)DecStrToHexStr(<string>)+
returns the hexadecimal representation of the input string if this was the decimal representation of an integer. In case of a converting error the function returns a empty string.
base64EncodeStr(<string>)base64DecodeStr(<string>)RandomStrCompareDotSeparatedNumbers(<string1>, <string2>)Example:
The Code:
comment "Testing: "
message "CompareDotSeparatedNumbers"
set $string1$ = "1.2.3.4.5"
set $string2$ = "1.2.3.4.5"
set $ConstTest$ = "0"
set $CompValue$ = CompareDotSeparatedNumbers($string1$, $string2$)
if ($ConstTest$ = $CompValue$)
comment "passed"
comment $string1$+" is equal to "+$string2$
else
set $TestResult$ = "not o.k."
LogWarning "failed"
endif
set $string1$ = "1.2.31.4.5"
set $string2$ = "1.2.13.4.5"
set $ConstTest$ = "1"
set $CompValue$ = CompareDotSeparatedNumbers($string1$, $string2$)
if ($ConstTest$ = $CompValue$)
comment "passed"
comment $string1$+" is higher then "+$string2$
else
set $TestResult$ = "not o.k."
LogWarning "failed"
endif
set $string1$ = "1.2.3.4.5"
set $string2$ = "1.2.13.4.5"
set $ConstTest$ = "-1"
set $CompValue$ = CompareDotSeparatedNumbers($string1$, $string2$)
if ($ConstTest$ = $CompValue$)
comment "passed"
comment $string1$+" is lower then "+$string2$
else
set $TestResult$ = "not o.k."
LogWarning "failed"
endif
comment ""
comment "-------------------------------------"
comment "Testing: "
message "CompareDotSeparatedStrings"
set $string1$ = "1.a.b.c.3"
set $string2$ = "1.a.b.c.3"
set $ConstTest$ = "0"
set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$)
if ($ConstTest$ = $CompValue$)
comment "passed"
comment $string1$+" is equal to "+$string2$
else
set $TestResult$ = "not o.k."
LogWarning "failed"
endifleads to the following log:
comment: Testing: message CompareDotSeparatedNumbers Set $string1$ = "1.2.3.4.5" The value of the variable "$string1$" is now: "1.2.3.4.5" Set $string2$ = "1.2.3.4.5" The value of the variable "$string2$" is now: "1.2.3.4.5" Set $ConstTest$ = "0" The value of the variable "$ConstTest$" is now: "0" Set $CompValue$ = CompareDotSeparatedNumbers($string1$, $string2$) The value of the variable "$CompValue$" is now: "0" If $ConstTest$ = $CompValue$ <<< result true ($ConstTest$ = $CompValue$) <<< result true Then comment: passed comment: 1.2.3.4.5 is equal to 1.2.3.4.5 Else EndIf Set $string1$ = "1.2.31.4.5" The value of the variable "$string1$" is now: "1.2.31.4.5" Set $string2$ = "1.2.13.4.5" The value of the variable "$string2$" is now: "1.2.13.4.5" Set $ConstTest$ = "1" The value of the variable "$ConstTest$" is now: "1" Set $CompValue$ = CompareDotSeparatedNumbers($string1$, $string2$) The value of the variable "$CompValue$" is now: "1" If $ConstTest$ = $CompValue$ <<< result true ($ConstTest$ = $CompValue$) <<< result true Then comment: passed comment: 1.2.31.4.5 is higher then 1.2.13.4.5 Else EndIf Set $string1$ = "1.2.3.4.5" The value of the variable "$string1$" is now: "1.2.3.4.5" Set $string2$ = "1.2.13.4.5" The value of the variable "$string2$" is now: "1.2.13.4.5" Set $ConstTest$ = "-1" The value of the variable "$ConstTest$" is now: "-1" Set $CompValue$ = CompareDotSeparatedNumbers($string1$, $string2$) The value of the variable "$CompValue$" is now: "-1" If $ConstTest$ = $CompValue$ <<< result true ($ConstTest$ = $CompValue$) <<< result true Then comment: passed comment: 1.2.3.4.5 is lower then 1.2.13.4.5 Else EndIf
CompareDotSeparatedStrings (<string1>, <string2>)Example:
The Code:
comment "Testing: "
message "CompareDotSeparatedStrings"
set $string1$ = "1.a.b.c.3"
set $string2$ = "1.a.b.c.3"
set $ConstTest$ = "0"
set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$)
if ($ConstTest$ = $CompValue$)
comment "passed"
comment $string1$+" is equal to "+$string2$
else
set $TestResult$ = "not o.k."
LogWarning "failed"
endif
set $string1$ = "1.a.b.c.3"
set $string2$ = "1.A.B.C.3"
set $ConstTest$ = "0"
set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$)
if ($ConstTest$ = $CompValue$)
comment "passed"
comment $string1$+" is equal to "+$string2$
else
set $TestResult$ = "not o.k."
LogWarning "failed"
endif
set $string1$ = "1.a.cb.c.3"
set $string2$ = "1.a.b.c.3"
set $ConstTest$ = "1"
set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$)
if ($ConstTest$ = $CompValue$)
comment "passed"
comment $string1$+" is higher then "+$string2$
else
set $TestResult$ = "not o.k."
LogWarning "failed"
endif
set $string1$ = "1.a.ab.c.3"
set $string2$ = "1.a.b.c.3"
set $ConstTest$ = "-1"
set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$)
if ($ConstTest$ = $CompValue$)
comment "passed"
comment $string1$+" is lower then "+$string2$
else
set $TestResult$ = "not o.k."
LogWarning "failed"
endif
set $string1$ = "1.2.13.4.5"
set $string2$ = "1.2.3.4.5"
set $ConstTest$ = "-1"
set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$)
if ($ConstTest$ = $CompValue$)
comment "passed"
comment $string1$+" is lower then "+$string2$
comment "using CompareDotSeparatedStrings give wrong results on numbers"
else
set $TestResult$ = "not o.k."
LogWarning "failed"
endif
set $string1$ = "1.2.3.4.5"
set $string2$ = "1.2.13.4.5"
set $ConstTest$ = "1"
set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$)
if ($ConstTest$ = $CompValue$)
comment "passed"
comment $string1$+" is higher then "+$string2$
comment "using CompareDotSeparatedStrings give wrong results on numbers"
else
set $TestResult$ = "not o.k."
LogWarning "failed"
endifleads to the following log:
comment: Testing: message CompareDotSeparatedStrings Set $string1$ = "1.a.b.c.3" The value of the variable "$string1$" is now: "1.a.b.c.3" Set $string2$ = "1.a.b.c.3" The value of the variable "$string2$" is now: "1.a.b.c.3" Set $ConstTest$ = "0" The value of the variable "$ConstTest$" is now: "0" Set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$) The value of the variable "$CompValue$" is now: "0" If $ConstTest$ = $CompValue$ <<< result true ($ConstTest$ = $CompValue$) <<< result true Then comment: passed comment: 1.a.b.c.3 is equal to 1.a.b.c.3 Else EndIf Set $string1$ = "1.a.b.c.3" The value of the variable "$string1$" is now: "1.a.b.c.3" Set $string2$ = "1.A.B.C.3" The value of the variable "$string2$" is now: "1.A.B.C.3" Set $ConstTest$ = "0" The value of the variable "$ConstTest$" is now: "0" Set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$) The value of the variable "$CompValue$" is now: "0" If $ConstTest$ = $CompValue$ <<< result true ($ConstTest$ = $CompValue$) <<< result true Then comment: passed comment: 1.a.b.c.3 is equal to 1.A.B.C.3 Else EndIf Set $string1$ = "1.a.cb.c.3" The value of the variable "$string1$" is now: "1.a.cb.c.3" Set $string2$ = "1.a.b.c.3" The value of the variable "$string2$" is now: "1.a.b.c.3" Set $ConstTest$ = "1" The value of the variable "$ConstTest$" is now: "1" Set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$) The value of the variable "$CompValue$" is now: "1" If $ConstTest$ = $CompValue$ <<< result true ($ConstTest$ = $CompValue$) <<< result true Then comment: passed comment: 1.a.cb.c.3 is higher then 1.a.b.c.3 Else EndIf Set $string1$ = "1.a.ab.c.3" The value of the variable "$string1$" is now: "1.a.ab.c.3" Set $string2$ = "1.a.b.c.3" The value of the variable "$string2$" is now: "1.a.b.c.3" Set $ConstTest$ = "-1" The value of the variable "$ConstTest$" is now: "-1" Set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$) The value of the variable "$CompValue$" is now: "-1" If $ConstTest$ = $CompValue$ <<< result true ($ConstTest$ = $CompValue$) <<< result true Then comment: passed comment: 1.a.ab.c.3 is lower then 1.a.b.c.3 Else EndIf Set $string1$ = "1.2.13.4.5" The value of the variable "$string1$" is now: "1.2.13.4.5" Set $string2$ = "1.2.3.4.5" The value of the variable "$string2$" is now: "1.2.3.4.5" Set $ConstTest$ = "-1" The value of the variable "$ConstTest$" is now: "-1" Set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$) The value of the variable "$CompValue$" is now: "-1" If $ConstTest$ = $CompValue$ <<< result true ($ConstTest$ = $CompValue$) <<< result true Then comment: passed comment: 1.2.13.4.5 is lower then 1.2.3.4.5 comment: using CompareDotSeparatedStrings give wrong results on numbers Else EndIf Set $string1$ = "1.2.3.4.5" The value of the variable "$string1$" is now: "1.2.3.4.5" Set $string2$ = "1.2.13.4.5" The value of the variable "$string2$" is now: "1.2.13.4.5" Set $ConstTest$ = "1" The value of the variable "$ConstTest$" is now: "1" Set $CompValue$ = CompareDotSeparatedStrings($string1$, $string2$) The value of the variable "$CompValue$" is now: "1" If $ConstTest$ = $CompValue$ <<< result true ($ConstTest$ = $CompValue$) <<< result true Then comment: passed comment: 1.2.3.4.5 is higher then 1.2.13.4.5 comment: using CompareDotSeparatedStrings give wrong results on numbers Else EndIf
DemandLicenseKey(`poolId [, productId [,windowsSoftwareId]])`Examples:
set $mykey$ = DemandLicenseKey ("pool_office2007")
set $mykey$ = DemandLicenseKey ("", "office2007")
set $mykey$ = DemandLicenseKey ("", "", "{3248F0A8-6813-11D6-A77B}")FreeLicense(`poolId [, productId [,windowsSoftwareId]]])`DemandLicenseKey
Example:
DefVar $opsiresult$
set $opsiresult$ = FreeLicense("pool_office2007")$opsiresult$ becomes the empty String, if no error occurred, and, if an error occurred, the error info text.
getLastServiceErrorClassgetLastServiceErrorMessageExample:
if getLastServiceErrorClass = "None"
comment "kein Fehler aufgetreten"
endifA String list (or a String list value) is a sequence of String values. For this kind of values we have the variable of type String list. They are defined by the statement
DefStringList <VarName>
A String list value may be assigned to String list variable:
Set <VarName> = <StringListValue>
String list values can be given only as results of String expressions. There are many ways to create or capture String lists, and many options for processing them, often yielding new String lists. They are presented in the following subsections.
For the following examples we declare a String list variable $list1$:
DefStringList $list1$
If we refer to variables named like String0, StringVal, .. it is meant that these represent any String expressions.
We start with a special and rather useful kind of String lists: maps – also called hashes or associative arrays – which consist of a lines of the form KEY=VALUE. In fact, each map should establish a function which associates a VALUE to a KEY, and any KEY should occur at most once as the first part of a line (whereas different KEY’s may be associated with identical 'VALUE parts).
getMSVersionMapThe Results from suite_mask and product_type_nr are integers that can be build by or operations of the following values.
product_type_nr
SuiteMask
Example:
The Code
DefStringList $INST_Resultlist$ DefStringList $INST_Resultlist2$ message "getMSVersionMap" comment "get value by winst function" set $INST_Resultlist$ = getMSVersionMap
produces the following log:
message getMSVersionMap
comment: get value by winst function
Set $INST_Resultlist$ = getMSVersionMap
retrieving strings from getMSVersionMap [switch to loglevel 7 for debugging]
(string 0)major_version=5
(string 1)minor_version=1
(string 2)build_number=2600
(string 3)platform_id=2
(string 4)csd_version=Service Pack 3
(string 5)service_pack_major=3
(string 6)service_pack_minor=0
(string 7)suite_mask=256
(string 8)product_type_nr=1
(string 9)2003r2=falseBackground infos for getMSVersionMap
getFileInfoMap(<FILENAME>)At this moment, there exist the keys,
Usage: If we define and call
DefStringList FileInfo DefVar $InterestingFile$ Set $InterestingFile$ = "c:\program files\my program.exe" set FileInfo = getFileInfoMap($InterestingFile$)
we get the value associated with key "FileVersion" from the call
DefVar $result$
set $result$ = getValue("FileVersion", FileInfo)(for the function getValue cf. Section 7.4.4, “Simple String Values generated from String Lists”).
Example:
The code:
set $InterestingFile$ = "%winstdir%\winst.exe"
if not (FileExists($InterestingFile$))
set $InterestingFile$ = "%winstdir%\winst32.exe"
endif
set $INST_Resultlist$ = getFileInfoMap($InterestingFile$)produce the log:
Set $InterestingFile$ = "N:\develop\delphi\winst32\trunk\winst.exe"
The value of the variable is now: "N:\develop\delphi\winst32\trunk\winst.exe"
If
Starting query if file exist ...
FileExists($InterestingFile$) <<< result true
not (FileExists($InterestingFile$)) <<< result false
Then
EndIf
Set $INST_Resultlist$ = getFileInfoMap($InterestingFile$)
retrieving strings from getFileInfoMap [switch to loglevel 7 for debugging]
(string 0)Language name 0=Deutsch (Deutschland)
(string 1)Language ID 0=1031
(string 2)file version=1125942857039872
(string 3)file version with dots=4.10.8.0
(string 4)product version=1125942857039872
(string 5)Comments=
(string 6)CompanyName=uib gmbh (www.uib.de)
(string 7)FileDescription=opsi.org
(string 8)FileVersion=4.10.8.0
(string 9)InternalName=
(string 10)LegalCopyright=uib gmbh under GPL
(string 11)LegalTrademarks=opsi
(string 12)OriginalFilename=
(string 13)PrivateBuild=
(string 14)ProductName=opsi-winst
(string 15)ProductVersion=4.0
(string 16)SpecialBuild=getLocaleInfoMapAt this moment, there exist the keys:
The system_default keys gives information about the language of the installed OS. The other keys give information about the locale of the GUI.
Example:
The code:
message "Locale Infos" set $INST_Resultlist$ = getLocaleInfoMap
produces e.g the log:
message Locale Infos
Set $INST_Resultlist$ = getLocaleInfoMap
retrieving strings from getLocaleInfoMap [switch to loglevel 7 for debugging]
(string 0)language_id_2chars=DE
(string 1)language_id=DEU
(string 2)localized_name_of_language=Deutsch (Deutschland)
(string 3)English_name_of_language=German
(string 4)abbreviated_language_name=DEU
(string 5)native_name_of_language=Deutsch
(string 6)country_code=49
(string 7)localized_name_of_country=Deutschland
(string 8)English_name_of_country=Germany
(string 9)abbreviated_country_name=DEU
(string 10)native_name_of_country=Deutschland
(string 11)default_language_id=0407
(string 12)default_language_id_decimal=1031
(string 13)default_country_code=49
(string 14)default_oem_code_page=850
(string 15)default_ansi_code_page=1252
(string 16)default_mac_code_page=10000
(string 17)system_default_language_id=0407
(string 18)system_default_posix=de_DE
(string 19)system_default_lang_region=de-DEUsage: If we define and call
DefStringList $languageInfo$ set $languageInfo$ = getLocaleInfoMap
we get the value associated with key "language_id_2chars" from the call
DefVar $result$
set $result$ = getValue("language_id_2chars", $languageInfo$)(for the function getValue cf. Section 7.4.4, “Simple String Values generated from String Lists”). We may now write scripts using a construct like
if getValue("language_id_2chars", languageInfo) = "DE"
; installiere deutsche Version
else
if getValue("language_id_2chars", languageInfo) = "EN"
; installiere englische Version
endif
endifBackground infos for getLocaleInfoMap:
getLocaleInfoGetLocaleInfoMap .
getProductMap // since 4.11.2.1Example:
set $INST_Resultlist$ = getProductMap
set $string1$ = getValue("id", $INST_Resultlist$)produces e.g the log:
Set $INST_Resultlist$ = getProductMap
retrieving strings from getProductMap [switch to loglevel 7 for debugging]
(string 0)id=opsi-winst-test
(string 1)name=opsi-winst test
(string 2)description=Test and example script for opsi-winst
(string 3)advice=
(string 4)productversion=4.11.2
(string 5)packageversion=1
(string 6)priority=0
(string 7)installationstate=unknown
(string 8)lastactionrequest=setup
(string 9)lastactionresult=successful
(string 10)installedversion=4.11.2
(string 11)installedpackage=1
(string 12)installedmodificationtime=
Set $string1$ = getValue("id", $INST_Resultlist$)
retrieving strings from $INST_Resultlist$ [switch to loglevel 7 for debugging]
(string 0)id=opsi-winst-test
(string 1)name=opsi-winst test
(string 2)description=Test and example script for opsi-winst
(string 3)advice=
(string 4)productversion=4.11.2
(string 5)packageversion=1
(string 6)priority=0
(string 7)installationstate=unknown
(string 8)lastactionrequest=setup
(string 9)lastactionresult=successful
(string 10)installedversion=4.11.2
(string 11)installedpackage=1
(string 12)installedmodificationtime=
The value of the variable "$string1$" is now: "opsi-winst-test"createStringList (`String0, String1 ,... `)set $list1$ = createStringList ('a','b', 'c', 'd')we get a list of the first four letters of the alphabet.
The following two functions produce a String list by splitting some string:
splitString (<string1>, <string2>)set $list1$ = splitString ("\\server\share\directory", "\")defines the list
"", "", "server", "share", "directory"
splitStringOnWhiteSpace (<string>)set $list1$ = splitStringOnWhiteSpace("Status Lokal Remote Netzwerk")produces the list
"Status", "Lokal", "Remote", "Netzwerk"
no matter how many blanks or tabs constitute the white space between the words.
loadTextFile (<file name>)loadUnicodeTextFile (<file name>)getSectionNames (<file name>)composeString (<string list>, <link string>)$line$ = composeString ($list1$, " | ")
we assign the value "a | b | c | d | e". to $line$.
takeString(<index>,<list>)
takeString (2, $list1$)
we get string "c" (since list counting starts with 0).
Negative values of index go downwards from the list count value. E.g.,
takeString (-1, $list1$)
return the last list element, that is "e".
takeFirstStringContaining(<list>,<search string>)getValue (<key>, <list>)getLocaleInfoMap and getFileVersionMap string list functions (cf. Section 7.4.1, “Info Maps”).
getValueBySeparator(<key string>,<separator string>,<hash string list> ) //since 4.11.2.1getValue but you have to give the <separator string> so that can also work with hashes likecount (<list>)count ($list1$) has the value "5".
retrieveSection (`section name)`getOutStreamFromSection (`section name)`DosInAnIcon (ShellInAnIcon),ExecWith and ExecPython calls – captures the output to standard out and standard error of the invoked commands writing them into a string list. For example:set $list$ = getOutStreamFromSection ('DosInAnIcon_netuse')
[DosInAnIcon_netuse]
net use$list1$ contains among some surrounding stuff the list of all mounted shares of a PC.
getReturnListFromSection (`section name)`XMLPatch sections and opsiServiceCall sections - there is a specific return statement which yields some result of the execution of the section (assumed to be of String list type).set list1 = getReturnListFromSection ('XMLPatch_mime "c:\mimetypes.rdf"')to get a specific knot list of the XML file mimetypes.rdf. (More info to XMLPatch sections at Section 8.7, “XMLPatch Sections” in this manual).
Or the list of opsi clients is produced by the reference to a opsi service call:
DefStringList $result$
Set $result$=getReturnListFromSection("opsiservicecall_clientIdsList")
[opsiservicecall_clientIdsList]
"method":"getClientIds_list"
"params":[]getSubList (<Startindex>, <Endindex>, <list> )set $list1$ = getSubList(1 : 3, $list$)
we get the partial list b, c, d . Begin index as well as end index have to be interpreted as the index of the first and last included list elements. The counting starts with 0.
Default start index is 0, default end index is the index of the last element of the list.
Therefore, (for the above defined list1) the command
set $list1$ = getSubList(1 : , $list$)
yields the list b, c, d, e.
set $list1$ = getSubList(:, $list$)
produces a copy of the original list.
It is possible to count backwards in order to determine the last index:
set $list1$ = getSubList(1 : -1, $list$)
defines the list of elements starting with the first and ending with the second to last element of the list – in the above example we again get list b, c, d.
getListContaining(<list>,<search string>)takeFirstStringContaining(<list>,<search string>)addtolist(<list>,<string>)addlisttolist(<list1>,<list2>)reverse (<list>)set $list1$ = reverse ($list$)
we get the $list1$ e, d, c, b, a.
An important usage of string lists is based on the possibility that the script runs through all elements of a list executing some operation on each string element.
The syntax to define this repetition is:
for %s% in <list> do <one statement | sub section>
This expression locally defines a string variable %s% that takes one by one the values of the list elements. <one statement> can be any single statement that can exist in a primary section or (and most interestingly) it may be a subsection call. The locally defined iteration index %s% exists in the whole context of statement, in particular in the subsection if statement is a subsection call.
The replacement mechanism for %s% always works like that for constants: The name of the variable is replaced by the element values. If we iterate through a list a,b,c and the iteration index is named %s%, we get for %s% one by one a, b, c – not the String values. To reproduce the original list elements we have to enclose %s% in citation marks.
Example: Let $list1$ be the list a, b, c, d, e, and $line$ a String variable. The statement
for %s% in $list1$ do set $line$ = $line$ + "%s%"
iterates through the list elements internally executing
$line$ = $line$ + "a" $line$ = $line$ + "b" $line$ = $line$ + "c" $line$ = $line$ + "d" $line$ = $line$ + "e"
Such, finally line has value abcde . If we omitted the citation marks around %s% we would get a syntax error for each iteration step.
Please note: The note variable is only valid in the directly called procedure. If it is needed in sub programs of it its value must be transferred to a global variable.
Killtask <process>
tries to stop all processes that execute the program named by the string expression.killtask "winword.exe"
sleepSeconds <Integer>markTimediffTimemarktime.
comment <string> or comment = <const string>LogError <string> or LogError = <const string>LogWarning <string> or LogWarning = <const string>includelog <file name> <tail size> //since 4.11.2.1includelog "%Scriptpath%\test-files\10lines.txt" "5"
Message <string expression>Message = <sequence of characters>message is set.Message "Installation von "+$productid$
ShowMessageFile <file name>ShowMessageFile "p:\login\day.msg"
one can realize a "Message of the Day" mechanism.
ShowBitMap [<image name>] [<inscription>]ShowBitmap "%scriptpath%\" + $ProductId$ + ".png" "$ProductId$"
Pause <string> or Pause = <const string>Stop <string> or stop = <const string>The opsi Roaming Profile Support is under co-funding. This means that these features are not free yet (05.10.2011)
GetScriptMode //since 4.11.2.1
give one of the possible values Machine,Login:
GetUserSID(<Windows Username>)
GetLoggedInUser //since 4.11.1.2
GetUsercontext //since 4.11.1.2saveVersionToProfile //since 4.11.2.1productversion-packageversion to local profilereadVersionFromProfile or scriptWasExecutedBefore. It marks that the userLoginScript for this product in this product version and package version was excuted for the actual user. The inrormation is saved at the file "%CurrentAppdataDir%\.opsi.org\userLoginScripts.ini"
readVersionFromProfile //since 4.11.2.1productversion-packageversion for the running opsi product which was read from local profile. See also: saveVersionToProfilescriptWasExecutedBefore //since 4.11.2.1scriptWasExecutedBefore checks if there is a version stamp in the profile (like you may do with the readVersionFromProfile command) It returns true if saved and running productversion-packageversion are identical. Then it set a new stamp to the profile (like you may do with the saveVersionToProfile command). So you may just use this single command in a if statement.isLoginScript //since 4.11.2.1GetScriptMode
In primary sections, the execution of a statement or a sequence of statements can be made dependent on some condition.
Example
;Which Windows version?
DefVar $MSVersion$
Set $MSVersion$ = GetMsVersionInfo
if ($MSVersion$>="6")
sub_install_win7
else
if ( $MSVersion$ = "5.1" )
sub_install_winXP
else
stop "not a supported OS-Version"
endif
endifThe syntax of the complete if statement is:
if <condition>
<sequence of statements>
else
<sequence of statements>
endif
The else part may be omitted.
if statements may be nested. That is, in the sequence of statements that depend on an if clause (no matter if inside the if or the else part) another if statement may occur.
<condition> is a <Boolean expression> . A Boolean (or logical) expression can be constructed as a (String) value comparison, by Boolean operators, or by certain function calls which evaluate to true or false. Up to now these Boolean values cannot be explicitly represented in a opsi-winst script).
The String comparison (which is a Boolean expression) has the form
<String expression> <comparison sign> <String expression>
where <comparison sign> is one of the signs
< <= = >= >
String comparisons in opsi-winst are case independent.
Inequality must be expressed by a NOT() expression which is presented below.
There is as well a comparison expression for comparing Strings as (integer) numbers. If any of them cannot be converted to a number an error will be indicated.
This number comparison expression has the same form as the String comparison but for an INT prefix of the comparison sign:
<String expression> INT<comparison sign> <String expression>
Such, we can build expressions as
if $Name1$ INT<= $Name2$
or
if $Number1$ INT>= $Number2$
Boolean operators are AND, OR, and NOT() (case does not matter). If b1, b2 and b3 are Boolean expressions the combined expressions
b1 AND b2
b1 OR b2
NOT( b3 )
are Boolean expressions as well denoting respectively the conjunction (AND), the disjunction (OR) and the negation (NOT).
A Boolean expression can be enclosed in parentheses (such producing a new Boolean expression with the same value).
The common rules of Boolean operator priority ("and" before "or") are at this moment not implemented. An expression with more than one operator is interpreted from left to right. For clarity, in a Boolean expression that combines AND and OR operators parentheses should be employed, e.g. we should explicitly write
b1 OR (b2 AND b3)
or
(b1 OR b2) AND b3
The second example describes what would be executed if there were no parentheses - whereas the common interpretation would run as the other line indicates.
Boolean operators can be conceived as special Boolean valued functions (the negation operator demonstrates this very clearly).
There are some more Boolean functions implemented. Every call of such a function constitutes a Boolean expression as well:
FileExists(<file name>)FileExists32(<file name>) see Chapter 64 Bit support
FileExists64(<file name>) see Chapter 64 Bit support
FileExistsSysNative(<file name>) see Chapter 64 Bit support
LineExistsIn(<line>, <file name>)LineBeginning_ExistsIn(<string>, <file name>)XMLAddNamespace(<XMLfilename>, <XMLelementname>, <XMLnamespace>)XMLRemoveNamespace(<XMLfilename>, <XMLelementname>, <XMLnamespace>)HasMinimumSpace(<Laufwerksname>, <Kapazität>)if not (HasMinimumSpace ("%SYSTEMDRIVE%", "500 MB"))
LogError "Not enough space on %SystemDrive%, 500MB on drive %SystemDrive% needed"
isFatalError
endifopsiLicenseManagementEnabledrunningAsAdminStatements in primary sections which refer to instructions declared elsewhere are subprogram calls.,
if ($MSVersion$>="6")
sub_install_win7
else
if ( $MSVersion$ = "5.1" )
sub_install_winXP
else
stop "not a supported OS-Version"
endif
endifIn this example the statement:
sub_install_winXP
"calls" the section titled [sub_install_winXP] which is placed somewhere else in the script. E.g. we may have
[sub_install_winXP] Files_copy_XP WinBatch_SetupXP
Generally, there are three ways to place the referred instructions:
We describe the syntax of sub program calls in detail:
Formally, the syntax can be given by
<proc. type>(<proc. name> | <External proc. file> | <String list function> )
This expression may supplemented by one ore ore parameters (procedure type dependent).
That means: A procedure call consists of three main parts.
The first part is the subprogram type specifier.
Examples of type names are Sub (we call a procedure of type sub that is a again a primary section) or Files and WinBatch (calls of special secondary sections). The complete overview of the existing sub program types is given at Section 7.10, “Subprogram Calls”.
The second part determines where and how the lines of sub program are to be found.
sub_install_winXPfiles_copy_winXPsub "p:\install\opsiutils\mainroutine.ins"
tries to execute the lines of mainroutine.ins as statements of a sub section.
registry loadUnicodeTextFile("%scriptpath%/opsiorgkey.reg") /regeditSyntactically, this line is composed of three main parts:
* registry, the core statement specifying the section type,
* loadUnicodeTextFile(...), a String list expression specifying how to get the lines of a registry section resp. its surrogate.
* /regedit, parametrizing the registry call.
In this example, the call parameter already gives an example for the third part of a subsection call:
The third part of a procedure call comprises type specific call options.
For a reference of the call options cf. the descriptions of the section calls in Chapter 8, Secondary Sections.
The command ExitWindows is used to control reboots , shutdown and similar actions which should take place after the opsi-winst it self is terminated. The name of the command and the fact that there is no ExitWindows without modifier has histrical reasons: Working on Windows 3.1 you could exit windows to go back to the DOS level.
ExitWindows /RebootWantedExitWindows /Reboot (since otherwise an installation could fail because a required product is not yet completely installed).
ExitWindows /RebootExitWindows /ImmediateRebootExitWindows /ImmediateLogoutExitWindows /ShutdownWantedHow flags may be set to ensure that the script does not run into an infinite loop when ExitWindows /ImmediateReboot is called we demonstrate by the following code fragment:
DefVar $OS$
DefVar $Flag$
DefVar $WinstRegKey$
DefVar $RebootRegVar$
set $OS$=EnvVar("OS")
if $OS$="Windows_NT"
Set $WinstRegKey$ = "HKLM\SOFTWARE\opsi.org\winst"
Set $Flag$ = GetRegistryStringValue("["+$WinstRegKey$+"] "+"RebootFlag")
if not ($Flag$ = "1")
;=========================
; Statements BEFORE Reboot
Files_doSomething
; initialize reboot ...
Set $Flag$ = "1"
Registry_SaveRebootFlag
ExitWindows /ImmediateReboot
else
;=========================
; Statements AFTER Reboot
; set back reboot flag
Set $Flag$ = "0"
Registry_SaveRebootFlag
; the work part after reboot:
Files_doMore
endif
endif
[Registry_SaveRebootFlag]
openKey [$WinstRegKey$]
set "RebootFlag" = "$Flag$"
[Files_doSomething]
; a section executed before reboot
[Files_doMore]
; a section executed after rebootIf a product installation fails this should be sinaled to the server.
According to the fact that there is no automatism to detect a failed installation this have to be done by script commands.
To indicate in a opsi-winst script that the installation is failed we have to call the statement:
isFatalError
If this statement is called opsi-winst stops the normal execution of the script and sets the product result to failed (otherwise it is success).
E. g. , a "fatal error" shall be triggered if there is as much space left as it is needed for an installation:
DefVar $SpaceNeeded$"
Set $SpaceNeeded$" = "200 MB"
DefVar $LogErrorMessage$
Set $LogErrorMessage$ = "Not enough space on drive . Required "
Set $LogErrorMessage$ = $LogErrorMessage$ + $SpaceNeeded$"
if not(HasMinimumSpace ("%SYSTEMDRIVE%", $SpaceNeeded$))
LogError $LogErrorMessage$
isFatalError
; finish execution and set ProductState to failed
else
; we start the installation
; ...
endifIt is also possible to state
isFatalError
depending on the number of errors which occured in some critical part of an installation script. In order to do this we initialize the error counting by the command
* markErrorNumber
The number of execution errors which occur after setting the counter can be queried by the the number valued function
* errorsOccuredSinceMark
We can evaluate the result in a numerical comparison condition (that as yet is only implemented for this expression). E. g. we may state
* if errorsOccuredSinceMark > 0
and may, if this seems to make sense, then state
isFatalError
For increasing the number of counted errors depending on certain circumstances (that do not directly produce an error) we may use the logError statement.
We may test this by the following script example:
markErrorNumber
; Errors occuring after this mark are counted and
; will possibly be regarded as fatal
logError "test error"
; we write "test error" into the log file
; and increase the number of errors by 1
; for testing, comment out this line
if errorsOccuredSinceMark > 0
; we finish script execution as quick as possible
; and set the product state to "failed"
isFatalError
; but comment writing is not stopped
comment "error occured"
else
; no error occured, lets log this:
comment "no error occured"
endifThe secondary sections can be called from any primary section but have a different syntax. The syntax is derived from the functional requirements and library conditions and conventions for the specific purposes. Therefore from a secondary section, no further section can be called.
Secondary sections are specific each for a certain functional area. This refers to the object of the functionality, e.g. file system in general, the Windows registry, or XML files. But it refers even more to the apparatus that is internally applied. This may be demonstrated by the the variants of the batch sections (which call external programs or scripts).
The functional context is mirrored in the specific syntax of the particular section type.
A Files section mainly offers functions which correspond to copy commands of the underlying operating system. The surplus value when using the opsi-winst commands is the detailed logging and checking of all operations when necessary. If wanted overwriting of files can be forbidden if newer versions of a file (e.g. an newer dll-file) are already installed on the system.
A simple Files section could be:
[Files_do_some_copying] copy -sV "p:\install\instnsc\netscape\*.*" "C:\netscape" copy -sV "p:\install\instnsc\windows\*.*" "%SYSTEMROOT%"
These commands cause that all files of the directory
p:\install\instnsc\netscape are copied to the directory C:\netscape, and then all files from p:\install\instnsc\windows to the windows system directory (its value is automatically inserted into the constant name %SYSTEMROOT%).
Option -s means that all subdirectories are copied as well, -V activates the version control for library files.
In most cases a Files section will be called without parameters.
There are only some special uses of Files sections where the target of copy actions is set or changed in a certain specified way. We have got the two optional parameters
/AllNTUserProfiles resp.
/AllNTUserSendTo
Both variants mean:
The called Files section is executed once for each local Windows NT user.
Every copy command in the section is associated with an user specific target directory.
In case other we need to build other user specific path names we can use the automatically set variable %UserProfileDir% or since opsi-winst version 4.11.2 %CurrentProfileDir%.
With option /AllNTUserProfiles the user specific target directory for copy actions is the user profile directory (that is usually denoted by the user name and is by default situated as a subdirectory of the userappdata directory. In case of option /AllNTUserSendTo the target directory is the path of the user specific SendTo folder (for links of the windows explorer context menu).
The exact rule for determining the target path for a copy command has three parts:
copy <source file(s)>copy <source file(s)> "%UserProfileDir%"copy <source file(s)> "%CurrentProfileDir%"
copy <source file(s)> <targetdir>copy <source file(s)> "%UserProfileDir%\targetdir"copy <source file(s)> "%CurrentProfileDir%\targetdir"
The use of %CurrentProfileDir% has the advantage that you may the same Files section with /AllNTUserProfiles if it is not running as userLoginScript (in Machine script mode) and without /AllNTUserProfiles if it is running as userLoginScript (in Login script mode).
There are also the Options:
/32Bit
/64Bit
/SysNative
which manipulate the file redirection on 64 Bit systems. For more details see Chapter 9, 64 Bit Support
In a Files section the following commands are defined:
Copy
Delete
del
SourcePath
CheckTargetPath
Copy and Delete roughly correspond the the Windows shell commands xcopy resp. del.
SourcePath and CheckTargetPath set origin and destination of the forthcoming copy actions (as if we would open two explorer windows for copy actions between them). If the target path does not exist it will be created.
The syntax definitions are:
Copy [-svdunxwnr] <source(mask)> <target path>
The source files can be denoted explicitly, using the wild card sign (”* ”) or by a directory name.
The <target path> is always understood as a directory name. Renaming by copying is not possible. If the target path does not exist it will be created (if needed a hierarchy of directories).
The optional modifiers of the Copy command mean (the ordering is insignificant):
s → We recursive into subdirectories.
e → Empty Subdirectories.V → Version checkingv → (do not use)-V instead.
d → With date check:u → We are only updating files:x → x-tractw → weakn → no over writec → continuer → read-only AttributeDelete [-sfd[n]] <path>
or
Delete [-sfd[n]] <source(mask)>
deletes files and directories.
Possible options are (with arbitrary ordering)
s → subdirectories
We recurse into subdirectories. Everything that matches the path name or the source mask is deleted.
The command
delete -s c:\opsi
Do not mean: remove the directory c:\opsi recursive, but it means: delete starting frm c:\ all occurences of opsi. This may lead to a complete hard disk scan.
If you want to delete the directory c:\opsi recursive use the command:
delete -s c:\opsi\
by using a trailing backslash you define that opsi is a directory.
It is safer to use the command del instead.
f → force
d [n] → date
Only files of age n days or older are deleted. n defaults to 1.
del [Options] <path[/mask]] //since 4.11.2.1delete but ondel -s -f c:\not-existsc:\not-exists not exists it do not search complete c:\ for not-exits
Example (you may forget the trailing Backslash):
del -sf c:\delete_this_dir
SourcePath = < source directory>Copy and (!) Delete commands.
CheckTargetPath = <target directory>Copy command . If the specified path does not exist it will be created.
A Patches section modifies a property file in ini file format. I. e. a file that consists of sections which are a sequence of entries constructed as settings <variable> = <value>. where sections are characterized by headings which are bracketed names like [sectionname].
Patches_DUMMY.INI $HomeTestFiles$+"\dummy.ini" [Patches_dummy.ini] add [secdummy] dummy1=add1 add [secdummy] dummy2=add2 add [secdummy] dummy3=add3 add [secdummy] dummy4=add4 add [secdummy] dummy5=add5 add [secdummy] dummy6=add6 set [secdummy] dummy2=set1 addnew [secdummy] dummy1=addnew1 change [secdummy] dummy3=change1 del [secdummy] dummy4 Replace dummy6=add6 replace1=replace1
produces the following log:
Execution of Patches_DUMMY.INI
FILE C:\tmp\testFiles\dummy.ini
Info: This file does not exist and will be created
addEntry [secdummy] dummy1=add1
addSection [secdummy]
done
done
addEntry [secdummy] dummy2=add2
done
addEntry [secdummy] dummy3=add3
done
addEntry [secdummy] dummy4=add4
done
addEntry [secdummy] dummy5=add5
done
addEntry [secdummy] dummy6=add6
done
setEntry [secdummy] dummy2=set1
Entry dummy2=add2
changed to dummy2=set1
addNewEntry [secdummy] dummy1=addnew1
appended entry
changeEntry [secdummy] dummy3=change1
entry dummy3=add3
changed to dummy3=change1
delEntry [secdummy] dummy4
in section secdummy deleted dummy4=add4
replaceEntrydummy6=add6 replace1=replace1
replaced in line 7
C:\tmp\testFiles\dummy.ini saved backFor more examples, please check the opsi standard product opsi-winst-test and in this product the part $Flag_winst_patches$ = "on"
As shown in the example the name of the property file to be patched is specified as parameter of the sub program call.
For a Patches section, we have commands:
add
set
addnew
change
del
delsec
replace
Each command refers to some section of the file which is to be patched. The name of this section is specified in brackets [] (which do here not mean "syntactically optional"!!).
In detail:
add [<section name>] <variable1> = <value1>set [<section name>]<variable1> = <value1>addnew [<section name>]<variable1> = <value1>change [<section name>]<variable1> = <value1>del [<section name>] <variable1> = <value1>del [<section name>] <variable1>delsec [<section name>]replace <variable1>=<value1> <variable2>=<value2>By virtue of a PatchHosts section we are able to modify a hosts file which is to understand as any file with lines having format
IPadress hostName aliases # comment
Aliases and comment (and the comment separator #) are optional. A line may also be a comment line starting with # .
The file which is to be modified can be given as parameter of a PatchHosts call. If there is no parameter a file named HOSTS is searched in the directories c:\nfs, c:\windows and %systemroot%\system32\drivers\etc. If no such file is found the PatchHosts call terminates with an error.
In a PatchHosts section there are defined commands:
setAddr
setName
setAlias
delAlias
delHost
setComment
Example:
PatchHosts_add $HomeTestFiles$+"\hosts" [PatchHosts_add] setAddr ServerNo1 111.111.111.111 setName 222.222.222.222 ServerNo2 setAlias ServerNo1 myServerNo1 setAlias 222.222.222.222 myServerNo2 setComment myServerNo2 Hallo Welt
produces the following log:
Execution of PatchHosts_add FILE C:\tmp\testFiles\hosts Set ipAddress 111.111.111.111 Hostname "ServerNo1" Set Hostname "ServerNo2" for ipAddress 222.222.222.222 Alias "myServerNo1" set for entry "ServerNo1" Alias "myServerNo2" set for entry "222.222.222.222" SetComment of Host "myServerNo2" to "Hallo Welt" C:\tmp\testFiles\hosts saved back
For more examples, please check the opsi standard product opsi-winst-test and in this product the part $Flag_winst_patch_hosts$ = "on".
In detail:
setaddr <hostname> <ipaddresse>setname <ipaddresse> <hostname>setalias <hostname> <alias>setalias <IPadresse> <alias>delalias <hostname> <alias>delalias <IPadresse> <alias>delhost <hostname>
removes the complete entry for the host with name <hostname>.
delhost <IPadresse>setComment <ident> <comment>A IdapiConfig section were designed to write parameters in idapi*.cfg files which are used by the Borland Database Engine.
This section type is not supported any more.
A PatchTextFile section offers a variety of options to patch arbitrary configuration files which are given as common text files (i.e. they can be treated line by line).
An essential tool for working on text files is the check if a specific line is contained in a given file. For this purpose we have got the Boolean functions Line_ExistsIn and LineBeginning_ExistsIn (cf. Section 7.9.2, “Boolean Expressions”).
We have got two commands especially for patching Mozilla preferences files plus the two deprecated and more restricted older versions of these commands:
Set_Mozilla_Pref ("<preference type>", "<preference key>", "<preference value>")Set_Netscape_User_Pref ("<preference variable>", "<value>")AddStringListElement_To_Mozilla_Pref ("<preference type>", "<preference variable>", "<add value>")AddStringListElement_To_Netscape_User_Pref ("<preference variable>", "<add values list>")The other commands of PatchTextFile sections are not file type specific. All operations are based on the concept that a line pointer exists which can be moved from top of the file i.e. above the top line down to the bottom (line).
There are three search commands:
FindLine <search string>
FindLine_StartingWith <search string>
FindLine_Containing <search string>
Each command starts searching at the current position of the line pointer. If they find a matching line the line pointer is moved to it. Otherwise the line pointer keeps its position.
<search string> - as all other String references in the following commands - are String surrounded by single or double citation marks.
GoToTop(when we count lines it has to be noted that this commands move the line pointer above the top line). We step any - positive or negative - number of lines through the file by
AdvanceLine [line count]GoToBottomBy the following command :
DeleteTheLineDeleteAllLines_StartingWith <search string>AddLine <line> or Add_Line <line>InsertLine <line> or Insert_Line <line>AppendLine <line>`or `Append_Line <line>Append_File <file name>Subtract_File <file name>SaveToFile <file name>SortedIn a LinkFolder section start menus entries as well as desktop links are managed.
E.g. the following section creates a folder named "acrobat“ in the common start menu (shared by all users):
[LinkFolder_Acrobat] set_basefolder common_programs set_subfolder "acrobat" set_link name: Acrobat Reader target: C:\Programme\adobe\Acrobat\reader\acrord32.exe parameters: working_dir: C:\Programme\adobe\Acrobat\reader icon_file: icon_index: end_link
As can be seen in the example, in a LinkFolder section the first thing to set is the virtual system folder on which the following statements shall operate:
set_basefolder <system folder>
The predefined virtual system folders which can be used are:
desktop, sendto, startmenu, startup, programs, desktopdirectory, common_startmenu, common_programs, common_startup, common_desktopdirectory
The folders are virtual since the operating system (resp. registry entries) determine the real places of them in the file system.
Second, we have to open a subfolder of the selected virtual folder:
set_subfolder <folder path>
The subfolder name is to be interpreted as a path name with the selected virtual system folder as root. If some link shall be directly placed into the system folder we have to write
set_subfolder ""
In the third step, we can start setting links. The command is a multi line expression starting with
set_link
and finished by
end_link.
Between these lines the link parameters are defined in the following format:
set_link
name: [link name]
target: <complete program path>
parameters: [command line parameters of the program]
working_dir: [working directory]
icon_file: [icon file path]
icon_index: [position of the icon in the icon file]
end_link
The target name is the only essential entry. The other entries have default values:
name defaults to the program name.
icon_file defaults to the target.
If the referenced target does not lie on an mounted share at the moment of link creation windows shortens its name to the 8.3 format.
Workaround:
Create a correct link when the share is connected.
Copy the ready link file to a location which exists at script runtime.
Let this file be the target.
delete_element <Linkname>delete_subfolder <Folderpath>set $list2$ = createStringList ('common_startmenu', 'common_programs', 'common_startup', 'common_desktopdirectory')
for $var$ in $list2$ do LinkFolder_Dummy
[LinkFolder_Dummy]
set_basefolder $var$
set_subfolder "Dummy"
set_link
name: Dummy
target: C:\Programme\PuTTY\putty.exe
parameters:
working_dir: C:\Programme\PuTTY
icon_file:
icon_index:
end_linkproduces the following log:
Set $list2$ = createStringList ('common_startmenu', 'common_programs', 'common_startup', 'common_desktopdirectory')
retrieving strings from createStringList [switch to loglevel 7 for debugging]
(string 0)common_startmenu
(string 1)common_programs
(string 2)common_startup
(string 3)common_desktopdirectory
retrieving strings from $list2$ [switch to loglevel 7 for debugging]
(string 0)common_startmenu
(string 1)common_programs
(string 2)common_startup
(string 3)common_desktopdirectory
~~~~~~ Looping through: 'common_startmenu', 'common_programs', 'common_startup', 'common_desktopdirectory'
Execution of LinkFolder_Dummy
Base folder is the COMMON STARTMENU folder
Created "Dummy" in the COMMON STARTMENU folder
ShellLink "Dummy" created
Execution of LinkFolder_Dummy
Base folder is the COMMON PROGRAMS folder
Created "Dummy" in the COMMON PROGRAMS folder
ShellLink "Dummy" created
Execution of LinkFolder_Dummy
Base folder is the COMMON STARTUP folder
Created "Dummy" in the COMMON STARTUP folder
ShellLink "Dummy" created
Execution of LinkFolder_Dummy
Base folder is the COMMON DESKTOPDIRECTORY folder
Created "Dummy" in the COMMON DESKTOPDIRECTORY folder
ShellLink "Dummy" created
~~~~~~ End LoopFor more examples, please check the opsi standard product opsi-winst-test and in this product the part $Flag_winst_link_folder$ = "on".
Today, the most popular way to keep configuration data or data at all is a file in XML document format. Its syntax follows the conventions as defined in the XML (or "Extended Markup Language") specification (http://www.w3.org/TR/xml/).
opsi-winst offers XMLPatch sections for editing XML documents.
With the actions defined for this section type opsi-winst can
When calling an XMLPatch section the document path name is given as parameter, e.g.
XMLPatch_mozilla_mimetypes $mozillaprofilepath$ + "\mimetypes.rdf"
A XML document logically describes a "tree" which starting from a "root" - therefore named document root– grows into branches. Every branch is labelled a node. The sub nodes of some node are called children or child nodes of their parent node.
In XML, the tree is constructed from elements. The beginning of any element description is marked by a tag (similarly as in HTML) i.e. a specific piece of text which is set into a pair of angle brackets ("<“ ">“, The end of the element description is defined by the the same tag text but now bracket by "</“ and „>“. If an element has no subordinated elements then there is no space needed between start tag and end tag. In this case the two tags can be combined to one with end bracket "/>“.
This sketch shows a simple "V"-tree - just one branching at the root level, rotated so that the root is top:
| root node (level 0) / \ node 1 and node 2 both on level 1 . . implicitly given end nodes below level 1
This tree could be described in XML in the following way:
<?xml version="1.0"?>
<root>
<node_level_1_no_1>
</node_level_1_no_1>
<node_level_1_no_2>
</node_level_1_no_2>
</root>The first line has to declare the XML version used. The rest of lines describe the tree.
So long the structure seems to be simple. But yet we have only "main nodes" each defining an element of the tree and marked by a pair of tags. But each main node may have subnodes of several kinds.
Of course, an element may have subordered elements, e.g. we may have subnodes A to C of node 1:
<node_level_1_no_1>
<node_level_2_A>
</node_level_2_A>
<node_level_2_B>
</node_level_2_B>
<node_level_2_C>
</node_level_2_c>
</node_level_1_no_1>If there are no subordinated elements an element can have subordinated text. Then it is said that the element has a subordinated text node. Example
<node_level_1_no_2>hello world </node_level_1_no_2>
A line break placed in the text node is now interpreted as part of the text where otherwise it is only a means of displaying XML structure. To avoid a line break belonging to "hello world" we have to write
<node_level_1_no_2>hello world</node_level_1_no_2>
Every element (no matter if it has subordinated elements or subordinated text) is constituted as a main node with specific tags. It can be further specified by attributes, so called attribute nodes. For example, there may be attributes "colour" or "angle" that distinguish different nodes of level 1.
<node_level_1_no_1 colour="green" angle="65" </node_level_1_no_1>
For selecting a set of elements any kind of information can be used:
In opsi-winst, selection based on criteria (1) to (3) and (7) is implemented
Before any operation on the contents of a XML file the precise set of elements has to be determined on which it will be operated. The set is constructed step by step by defining the allowed paths through the XML tree. The finally remaining end points of the paths define the selected set.
The basic opsi-winst command is
OpenNodeSet
There two formats for defining the allowed paths a short and a long format .
Explicit Syntax. The more explicit syntax may be seen in the following example (for a more complex example Section 10.4, “XML File Patching: Setting Template Path for OpenOffice.org 2”):
openNodeSet
documentroot
all_childelements_with:
elementname:"define"
all_childelements_with:
elementname:"handler"
attribute: extension value="doc"
all_childelements_with:
elementname:"application"
endShort Syntax. The same node set is given by the line:
openNodeSet 'define /handler value="doc"/application /'
In this syntax, the slash separates the steps into to the tree structure which are denoted in the more explicit syntax each by an own description.
Selecting by Textual Content (only for explicit syntax). Given the explicit syntax we may select elements by the textual content of elements:
openNodeSet
documentroot
all_childelements_with:
all_childelements_with:
elementname:"description"
attribute:“type“ value=“browser“
attribute:“name“ value=“mozilla“
all_childelements_with:
elementname:"linkurl"
text:"http://www.mozilla.org"
endParametrizing Search Strategy. In the exemplary descriptions of XML tree traversals there remain several questions.
To answer these questions explicitly there are parameters for the OpenNodeSet command. The following lines show the default settings which can be varied by changing the Boolean values:
- error_when_no_node_existing false - warning_when_no_node_existing true - error_when_nodecount_greater_1 false - warning_when_nodecount_greater_1 false - create_when_node_not_existing false - attributes_strict false
With short syntax, parametrizing precedes the OpenNodeSet command and holds for all levels of the XML tree. With the explicit syntax the parameters may be set directly after the OpenNodeSet command or be newly set for each level. In particular the option „create when node not existing“ may be set for some levels but not for all.
There exists a bundle of commands which operate on a selected element set
In detail:
SetAttribute "attribute name" value="attribute value"SetAttribute "name" value="OpenOffice Writer"
On the contrary, the command
AddAttribute "attribute name" value="attribute value"AddAttribute "name" value="OpenOffice Writer"By
* DeleteAttribute "attribute name"
we remove the specified attribute from each element of the selected element set.
The command
* DeleteElement "element name"
removes all elements with main node name (tag name) element name from the opened element set.
Finally there exist two commands for setting resp. adding text nodes.:
SetText "Text"
and
AddText "Text"
SetText "rtf"By
SetText ""
we remove the text node completely.
The variant
AddText "rtf"
sets the text only if there was no text node given.
A XMLPatch section may return the retrieved informations to the calling primary section. The result always is a String list, and to get it, the call must done via the String list function getReturnListFromSection. E.g. we may have the following String list setting in an Actions section where we use a XMLPatch_mime section
DefStringList $list1$
set $list1$=getReturnListFromSection ('XMLPatch_mime "c:\mimetypes.rdf"')Inside the XMLPatch section we have return commands that determine the content of returned String list:
return elements+
fills the selected elements completely (element name and attributes) into the return list.
return attributesreturn elementnamesreturn attributenames
gives a list only of the attribute names.
return textreturn countingIn a WinBatch section any windows executable can be started.
E.g, we may start some existing setup program by the following line in a WinBatch section
[winbatch_install] "%scriptpath%\setup.exe"
It is deprecated but still supported that you may call – as from Windows explorer – a file of any type for which a program is registered.
There a several parameters of the WinBatch call which determine if (or how long) opsi-winst shall be wait for the started programs returning.
/WaitOnClose/LetThemGo/WaitOnClose. It is used if opsi-winst shall proceed while the started processes run in their own threads.
/WaitSeconds [number of seconds]If we do the call with parameter /WaitSeconds [number of seconds] then opsi-winst is waiting the specified time before proceeding. In the default configuration we additionally wait for the started programs returning. If we combine the parameter with the option /LetThemGo then opsi-winst continues processing when the waiting time is finished.
/WaitForWindowAppearing [window title]/WaitForWindowVanish [window title]/WaitForProcessEndingThe first option means that opsi-winst waits until any process lets pop up a window with title window title. With the second option opsi-winst is waiting as long as a certain window (1) appeared on the desktop and (2) disappeared again.
CAUTION: These options only know windows of 32-bit programms
/WaitForProcessEnding <program name>/TimeOutSeconds.
/TimeOutSeconds <seconds>/WaitForProcessEnding)Winbatch_uninstall /WaitForProcessEnding "uninstall.exe" /TimeOutSeconds 20 [Winbatch_uninstall] "%ScriptPath%\uninstall_starter.exe"
getLastExitCodeVia DOSBatch (also called ShellBatch) sections a opsi-winst script uses Windows shell scripts for tasks which cannot be fulfilled by internal commands or for which already a batch script solution exists.
opsi-winst waits until the DOS-batch ends, before it is proceeding with the next script-section.
A DOSBatch section is simply processed by writing the lines of the sections into the file _winst.bat in c:\tmp and then calling this file in the context of a cmd.exe shell. This explains that a DosBatch section may contain all Windows shell commands can be used.
Compared with calling a cmd-file in a WinBatch section, the DOSBatch section
presents certain advantages:
The section type DOSInAnIcon or ShellInAnIcon is identical to DOSBatch regarding syntax and execution method but has a different appearance:
For DOSInAnIcon, a shell process is created with view set to minimized. That has the consequence that it is executed "in an icon". No command window appears, user interaction is suppressed.
Don’t use commands that wait for user interaction.
There are two kinds of parameters which may be passed to the section call:
The calling syntax is:
Sektionsname [batch parameter] [winst [modifier]]
Possible winst modifier are (since 4.11.1):
/32bit
/64bit
/Sysnative
These winst modifier have to be separated by the key word winst from the other parameters.
Other parameters of a DosBatch section are directly passed as quasi command line parameters to the Windows shell script.
E. g. we may call DosBatch_1 in Actions section to get a "Hello World" from the DOS echo command:
[Actions] DosBatch_1 today we say "Hello World" [DosBatch_1] @echo off echo %1 %2 %3 %4 pause
the execution of the Dos-Batch command echo with parameters today we say "Hello World".
The next example will be on a 64 bit system executed in a 64 bit cmd.exe and gives the output today we say:
[Actions] DosBatch_1 today we say winst /64bit [DosBatch_1] @echo off echo %1 %2 %3 %4 pause
The output of the shell commands can be captured by using the string list function getOutStreamFromSection() from the opsi-winst-scripts main-section
see also:
Section 7.4.4, “Simple String Values generated from String Lists”).
If the return list shall be evaluated programmatically it is advised to use the @ prefix of commands. Such we suppress the repetition of the command line in the output which may different formats dependent on system configurations.
By a Registry section call we can create, patch and delete entries in the Windows registry. As usual, opsi-winst logs every operation in detail as long as logging is not turned off.
Let us set some registry variables by a call to the section Registry_TestPatch where the section is given by
[Registry_TestPatch] openkey [HKEY_Current_User\Environment\Test] set "Testvar1" = "c:\rutils;%Systemroot%\hey" set "Testvar2" = REG_DWORD:0001
For further examples see the product opsi-winst-test and there look at $Flag_subregistry$ = "on"
/AllNTUserDats/AllNTUserDats
Further parameters control which syntactical variant of the Registry section shall be valid:
/regedit/regedit declares that the syntax corresponds the export file syntax of the Windows Registry Editor regedit. Such, the lines of a regedit export file may directly be used as a Registry resp. the file itself can serve as an external section (see Section 8.11.5, “Registry Sections in Regedit Format”).
/addReg/addReg declares that the Registry section syntax is that of an inf-file (as used e.g. for driver installations)
(see Section 8.11.6, “Registry Sections in AddReg Format”).
These not opsi-winst specific syntactical variants are not defined in this manual since they usually will be generated programmatically.
There are also the Options:
/32Bit/64Bit/SysNativewhich manipulate the registry write redirection on 64 Bit systems. (see Chapter 9, 64 Bit Support).
The default syntax of a Registry section is oriented at the command syntax of other patch operations in opsi-winst.
There exist the following commands:
OpenKey
Set
Add
Supp
GetMultiSZFromFile
SaveValueToFile
DeleteVar
DeleteKey
ReconstructFrom
Flushkey
In detail:
OpenKey <registry key>The registry key is denoted by a registry path name. Under regular circumstances it starts with one of the "high keys" which build the top level of the registry tree data structure (above the "root" ). These are: HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_CONFIG which may optionally be written as HKCR, HKCU, HKLM, HKU.
In opsi-winst syntax of the registry path name, the elements of a path are separated by single backslashs.
All other commands operate on an opened registry key.
Set <varname> = <value>If some registry variable shall be created or set which has not the default type Registry-String (REG_SZ) we have to use the extended variant of the Set command:
Set <varname> = <registry type>:<value>
sets the registry variable <varname> to value <value> of type <registry type>. The following registry types are supported:
set "myVariable" = REG_MULTI_SZ:"A|BC|de"
To construct a multistring we may put the strings as lines in a file and read it using GetMultiSZFromFile (cf. below).
Add <varname> = <value>
resp.
Add <varname> = <registry type> <value>
are analogous to the Set commands with the difference that entries are only added but values of existing variables not changed.
Supp <varname> <list separator> <supplement>Supp keeps the original string variant (REG_EXPAND_SZ or REG_SZ).Example:
The environment Path is determined by the value for the variable Path as defined inside the registry key
+ KEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
+ To add some entries to the path definition we have to get access to this key via an OpenKey. Then we can apply e.g.
+
supp "Path" ; "C:\utils;%JAVABIN%"
+ in order to supplement the path by "C:\utils" and "%JAVABIN%".
+ (Windows expands %JAVABIN% to the correct path name if %JAVABIN% exists as variable and the String is a REG_EXPAND_SZ.)
+ Whom read the old value of Path from the environment variable, write this value to the registry value - and are then able to work with the registry variable:
+
[Actions]
DefVar $Path$
set $Path$ = EnvVar ("Path")
Registry_PathPatch
[Registry_PathPatch]
openkey [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\control\Session Manager\Environment]
set "Path"="$Path$"
supp "Path"; "c:\orawin\bin"+ CAUTION: The environment variable gets a changed value after a reboot.
GetMultiSZFromFile <varname> <filename>SaveValueToFile <varname> <filename>DeleteVar <Varname>
DeleteKey <Registryschlüssel>
deletes the registry key recursively including all subkeys and contained variables. The registry key is defined as for OpenKey.
Example:
[Registry_Keydel] deletekey [HKCU\Environment\subkey1]
ReconstructFrom <filename>FlushKeyA Registry section called with parameter /AllNTUserdats is executed for every local user.
To this end, for all local users (as permanent storage for the registry branch HKEY_Users) the files NTUser.dat are searched one by one and temporarily loaded into a subkey of some registry branch. The commands of the registry section are executed for this subkey, then the subkey is unloaded. As result, the stored NTUser.dat is changed.
The mechanism does not work for a logged in user. For, his NTUser.dat is already in use, and the request to load it produces an error. To do the changes for him as well, the commands of the Registry additionally are executed on the HKEY_Users branch for the logged in user.
There is a NTUser.dat for Default User which serves as template for newly created users in the future. Therefore the patches are prepared for them as well.
The Registry section syntax remains unchanged. But the key pathes are interpreted relatively. This means leave away the main key
In the following example the registry entry for variable FileTransferEnabled is de facto set for all HKEY_Users\XX\Software… successive for all XX (all users) on the machine:
[Registry_AllUsers] openkey [Software\ORL\WinVNC3] set "FileTransferEnabled"=reg_dword:0x00000000
Since opsi-winst version 4.11.2 you may give the root key HKEY_CURRENT_USER at the openkey command.
Example:
[Registry_AllUsers] openkey [HKEY_CURRENT_USER\Software\ORL\WinVNC3] set "FileTransferEnabled"=reg_dword:0x00000000
This has the folloing advantages:
If a Registry section is called with parameter /regedit the section is not expected in opsi-winst standard format but in the format as produced by the Windows regedit tool.
The export files generated by regedit have - not regarding the head line - ini file format. Example:
REGEDIT4 [HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org] [HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\general] "bootmode"="BKSTD" "windomain"="" "opsiconf"=dword:00000001 [HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\shareinfo] "user"="pcpatch" "pcpatchpass"="" "depoturl"="\\\\bonifax\\opt_pcbin\\install" "configurl"="\\\\bonifax\\opt_pcbin\\pcpatch" "utilsurl"="\\\\bonifax\\opt_pcbin\\utils" "utilsdrive"="p:" "configdrive"="p:" "depotdrive"="p:"
The sections denote registry keys to be opened. Each line describes some variable setting like the set command in a opsi-winst registry section.
But, we cannot really have an internal opsi-winst section that is constructed from another sections. Therefore Registry section with parameter /regedit can only be given as external section or by the function call loadTextFile, e.g.
registry "%scriptpath%/opsiorgkey.reg" /regedit
With Windows XP the registry editor regedit does not produce Regedit4-Format but a new format that is indicated by the head line
"Windows Registry Editor Version 5.00"
In this format, Windows offers some additional value types. But more important, the export file is now generated in Unicode. opsi-winst sections processing is based on Delphi libraries which use 8 bit Strings. To work with a regedit 5 export the coding therefore has to converted. This can be done manually, e.g. by a suitable editor. But we may also feed the original file to opsi-winst using the String list function loadUnicodeTextFile. E.g., if printerconnections.reg be a unicode based export, we can call regedit in the following form which does the necessary code conversion on the fly:
registry loadUnicodeTextFile("%scriptpath%/opsiorgkey.reg") /regeditA registry patch using regedit format can as well be executed "for all NT users" similarly as the common opsi-winst registry section. That is, a path like [HKEY_CURRENT_USER\Software\ORL] is to replaced by the relative [Software\ORL].
A Registry section can be called with parameter /addReg. Then its syntax follows the principles of the [AddReg] sections in inf files as used e.g. for driver installations.
E.g.:
[Registry_ForAcroread] HKCR,".fdf","",0,"AcroExch.FDFDoc" HKCR,".pdf","",0,"AcroExch.Document"HKCR,"PDF.PdfCtrl.1","",0,"Acr"
This type of section allows to retrieve information – or set data – via the opsi service. There are three options for determining a connection to an opsi service:
Retrieved data may be returned as a String list and then used for scripting purposes.
The call parameters determine which opsi service will be addressed and set the connection parameters if needed.
Connection parameters can be defined via
/serviceurl <url to the opsi web service>
/username <web service user name>
/password <web service user password>
If these parameters, at least the serviceurl, are given opsi-winst tries to open a connection to an opsi service which has the url.
The additional option
/interactiveraises an interactive connect. The user will be asked for confirming the connection data and supplying the password. Of course, this option cannot be used in scripts which shall be executed fully automatically.
If no connection parameters are supplied opsi-winst assumes that an existing connection shall be reused.
If no connection parameters are given and the interactive option is not specified (neither at this call nor at a call earlier in the script) it is assumed that we are in a standard opsi boot process and, already having a connection to an opsi service, we try to address it.
/preloginservice/opsiclientd //since 4.11.2.1An opsiServiceCall, which uses an existing connection to an opsi-service, is defined by its method name and a list of parameters.
Both are defined in the section body. It has format
"method":<method name>
"params":[
<params>
]params is a (possibly empty) list of strings (comma-seperated). Use the parameters as required by the specified method.
E.g. we may have a section call where the required methodname and the (empty) list of parameters is set:
[opsiservicecall_clientIdsList] "method":"getClientIds_list" "params":[]
The section call produces the list of names (IDs) of all local opsi clients.
If the list shall be exploited for other than test purposes the section call can be used in a string list expression:
E.g.:
DefStringList $result$
Set $result$=getReturnListFromSection("opsiservicecall_clientIdsList")The usage of GetReturnListFromSection is documented in the string list function chapter of this manual (see Section 7.4.5, “Producing String Lists from opsi-winst Sections”).
A hash – in this case a string list, where each item is a pair name=value – is produced by the following opsi service call:
[opsiservicecall_hostHash]
"method": "getHost_hash"
"params": [
"pcbon8.uib.local"
]The sections opsiservicecall are developed for opsi 3.x methods. For opsi 4.x methods they are often not suitable. For example *_getIdents calls are possible, *_getObjects calls are not possible.
ExecPython sections are basically Shell-Sections (like DosInAnIcon) which call the – on the system installed – python script interpreter. It takes the section content as python script, and the section call parameter as parameters for the script.
Example:
The following example demonstrates a execPython call with a list of parameters for that are printed by the python commands.
The call may look like
execpython_hello -a "option a" -b "option b" "there we are"
[execpython_hello]
import sys
print "we are working in path: ", a
if len(sys.argv) > 1 :
for arg in sys.argv[1:] :
print arg
else:
print "no arguments"
print "hello"The print command output will be caught and written to the log file. So we get in the log
output:
--------------
-a
option a
-b
option b
there we are
helloObserve that the loglevel must be set at least to info (that is 1) if these outputs shall really find their way to the log file.
An execPython section is integrated with the surrounding opsi-winst script by four kinds of shared data:
An example for the first two ways of interweaving the python script with the opsi-winst script is already given above. We extend it to retrieve the values of some opsi-winst constants or variables.
[execpython_hello]
import sys
a = "%scriptpath%"
print "we are working in path: ", a
print "my host ID is ", "%hostID%"
if len(sys.argv) > 1 :
for arg in sys.argv[1:] :
print arg
else:
print "no arguments"
print "the current loglevel is ", "$loglevel$"
print "hello"Of course, the $loglevel$ variable has to be set beforehand in the Actions section:
DefVar $LogLevel$ set $loglevel$ = getLoglevel
Finally, in order to being able to use of some results of the section output, we produce it into a stringlist variable by calling the execPython section in the following way:
DefStringList pythonresult
Set pythonResult = GetOutStreamFromSection('execpython_hello -a "opt a“')ExecWith sections are more general than ExecPython or DosBatch sections: Which program interprets the section lines given is determined by a parameter of the section call.
E.g, if we have some call
execPython_hello -a "hello" -b "world"
where
-a "hello" -b "world"
are parameters that are passed to the python script we get the following completely equivalent ExecWith call:
execWith_hello "python" PASS -a "hello" -b "world" WINST /EscapeStrings
The option /EscapeStrings is automatically used in an ExecPython section and means that backslashes in String variables and constants are duplicated before interpretation by the the called program.
In general, we have the call syntax:
ExecWith_SECTION PROGRAM PROGRAMPARAS pass PASSPARAS winst WINSTOPTS
Each of the expressions PROGRAM, PROGRAMPARAS, PASSPARAS, WINSTOPTS may be an arbitrary String expression, or just a String constant (without citation marks).
The key words PASS and WINST may be missing if the respective parts do not exist.
There are two opsi-winst options recognized:
/EscapeStrings
/LetThemGo
Like with ExecPython sections, the output of an ExecWith section may be captured into a String list via the function getOutStreamFromSection.
The first one declares that the backslash in opsi-winst variables and constants is C-like escaped. The second one has the effect (as for winBatch calls) that the called program starts its work in new thread while opsi-winst is continuing to interpret its script.
The following call is meant to refer to a section which is an autoit3 script that waits for some upcoming window (therefore the option /letThemGo is used) in order to close it:
ExecWith_close "%SCRIPTPATH%\autoit3.exe" WINST /letThemGo
A simple
ExecWith_edit_me "notepad.exe" WINST /letThemGo
calls notepad and opens the section lines in it (but without any line that is starting with a semicolon since opsi-winst regards such lines as comments and eliminates them before handle).
For further examples watch the product opsi-winst-test and there especially $Flag_autoit3_test$ = "on"
A LDAPsearch section defines a search request to a LDAP directory, executes it and receives (and possibly caches) the response.
Before dwelling on the opsi-winst commands we do some explanations.
In subsection we give an example of the most probable usage of a LDAPsearch. The following subsections describe the syntax
LDAP, the "Lightweight Directory Access Protocol", is, as the name indicates, a defined way of communication to a directory. This directory is (or may be) hierarchically organized. That is, the directory is a hierarchical data base, or a tree of content.
A LDAP service implements the protocol. A directory that can be accessed via a LDAP service is called a LDAP directory.
For instance, let’s have a look at some part of the LDAP directory tree of an opsi server with LDAP backend (as shown by the Open Source tool JXplorer):
A LDAP search request is a query to a LDAP directory via a LDAP service. The response returns some content from the directory.
Basically the search request has to describe the path in the directory tree which leads to the interesting piece of information. The path is the distinguished name (dn), composed of the names of the nodes (the "relative distinguished names"), which build the path, for instance:
local/uib/opsi/generalConfigs/bonifax.uib.local
Since each node is conceived as an instance of some structural object class, the path description is usually given in the following form: with indication of the classes (and starting with the last path element) :
cn=bonifax.uib.local,cn=generalConfigs,cn=opsi,dc=uib,dc=local
The path in a query is not necessarily "complete", and not leading to a unique leaf of the tree. On the contrary, partial paths are common.
But even if the path descends to a unique leaf, the leaf may contain several values. Each node of the tree has one or more classes as attribute types. To each one or may values may be associated.
For a given query path, we therefore may be interested
Obviously, handling the amount of possibly returned response information is the main challenge when dealing with LDAP searches.
The following section shows the documentation of a LDAPsearch roughly corresponding to the screenshot above as executed by opsi-winst.
Example
Using the opsi-winst section ldapsearch_generalConfigs:
[ldapsearch_generalConfigs] targethost: bonifax dn: cn=generalConfigs,cn=opsi,dc=uib,dc=local
we will get a answer like this:
Result: 0
Object: cn=generalConfigs,cn=opsi,dc=uib,dc=local
Attribute: cn
generalConfigs
Attribute: objectClass
organizationalRole
Result: 1
Object: cn=pcbon4.uib.local,cn=generalConfigs,cn=opsi,dc=uib,dc=local
Attribute: cn
pcbon4.uib.local
Attribute: objectClass
opsiGeneralConfig
Attribute: opsiKeyValuePair
test2=test
test=a b c d
Result: 2
Object: cn=bonifax.uib.local,cn=generalConfigs,cn=opsi,dc=uib,dc=local
Attribute: objectClass
opsiGeneralConfig
Attribute: cn
bonifax.uib.local
Attribute: opsiKeyValuePair
opsiclientsideconfigcaching=FALSE
pcptchlabel1=opsi.org
pcptchlabel2=uib gmbh
button_stopnetworking=
pcptchbitmap1=winst1.bmp
pcptchbitmap2=winst2.bmp
debug=on
secsuntilconnectiontimeout=280
opsiclientd.global.log_level=There are several opsi-winst options to manage and reduce the complexity of the evaluation of such responses.
Two kinds of LDAPsearch parameters,
are defined for the call of LDAPsearch section.
The cache options are:
/cache
/cached
/free
If there is no cache option specified, the response of the LDAP search request is not saved for further usages.
By the /cache option, the response is cached for further evaluations, the /cached option refers to the last cached response which is reused instead of starting a new search, the /free option clears the cache explicitly (may only be useful for searches with extreme large responses).
The output options are:
/objects
/attributes
/values
The output options determine the String list that is produced when a LDAPsearch section is called via getReturnlistFromSection:
Observe that in the produced lists the object an attribute belongs to is only identifiable if only one object is returned in the object list, and likewise the object and the attribute to which a value is subsumed are only identifiable if there is only attribute remaining in the attributes list.
Such the proceeding is, that the LDAPsearch is specified up to that degree, that at most one object and one attribute is returned. This can be checked by a count call on the objects and the attributes return list. Then any value found belongs to the dn and the attribute specified.
The repeated utilization of the same LDAP response can be done without relevant time costs by using the cache/cached options.
An example may show how we can narrow the search to pin down a specific result from a LDAP directory.
We start with the call of ldapsearch_generalConfigs as above, only adding the cache parameter.
ldapsearch_generalconfigs /cache
executes the query and caches the response for further utilization.
Then, the call
getReturnlistFromSection("ldapsearch_generalconfigs /cached /objects")
produces the list
cn=generalconfigs,cn=opsi,dc=uib,dc=local cn=pcbon4.uib.local,cn=generalconfigs,cn=opsi,dc=uib,dc=local cn=bonifax.uib.local,cn=generalconfigs,cn=opsi,dc=uib,dc=local
If we narrow the tree selection by
[ldapsearch_generalConfigs] targethost: bonifax dn: cn=bonifax.ubi.local,cn=generalConfigs,cn=opsi,dc=uib,dc=local
and start again, then in the objects list, we indeed retain just
cn=bonifax.uib.local,cn=generalconfigs,cn=opsi,dc=uib,dc=local
The corresponding attributes list contains three elements:
objectclass cn opsikeyvaluepair
In order to get the values associated to a single attribute we have to confine the query once more:
[ldapsearch_generalConfigs] targethost: bonifax dn: cn=bonifax.ubi.local,cn=generalConfigs,cn=opsi,dc=uib,dc=local attribute: opsiKeyValuePair
The result now produced is an attributes list containing only one element. The corresponding values list looks like
opsiclientsideconfigcaching=false pcptchlabel1=opsi.org pcptchlabel2=uib gmbh button_stopnetworking= pcptchbitmap1=winst1.bmp pcptchbitmap2=winst2.bmp debug=on secsuntilconnectiontimeout=280 opsiclientd.global.log_level=6
There are no LDAP means to reduce this result furthermore!
(But the opsi-winst function getValue (key, list) (cf. Section 7.4.4, “Simple String Values generated from String Lists”) may help in this case: E.g.
getValue ("secsuntilconnectiontimeout", list)
would produces the requested number).
By the function count (list) we can check if we succeeded with the narrowing of the search request. In most circumstances, we would like that its result be "1".
A LDAPsearch section comprises the specifications:
targethost:targetport:dn:typesonly:filter:attributes:A short and rather realistic example shall end this section:
$founditems$ be a StringList variable and $opsiClient$ a String variable. The call of getReturnlistFromSection fetches the results of the section ldapsearch_hosts. The following code fragment returns the unique result for $opsiDescription$ if it exists. It reports an error if the search produces an unexpected result:
set $opsiClient$ = "test.uib.local"
set $founditems$ = getReturnlistFromSection("ldapsearch_hosts /values")
DefVar $opsiDescription$
set $opsiDescription$ = ""
if count(founditems) = "1"
set $opsiDescription$ = takeString(0, founditems)
else
if count(founditems) = "0"
comment "No result found")
else
logError "No unique result for LdAPsearch for client " + $opsiclient$
endif
endif
[ldapsearch_hosts]
targethost: opsiserver
targetport:
dn: cn=$opsiclient$,cn=hosts,cn=opsi,dc=uib,dc=local
typesOnly: false
filter: (objectclass=*)
attributes: opsiDescriptionFor further examples watch the product opsi-winst-test and there especially $Flag_winst_ldap_search$ = "on"
The opsi-winst is a 32 bit program. In order to make it easy for 32 bit programs to run on 64 bit systems there are special 32 bit areas in the registry as well in the file system. Some accesses from 32 bit programs will be redirected to these special areas to avoid access to areas that reserved for 64 bit programs.
A access to c:\windows\system32 will be redirected to c:\windows\syswow64
But a access to c:\program files will be not redirected to c:\program files (x86)
A registry access to [HKLM\software\opsi.org] will be redirected to [HKLM\software\wow6432node\opsi.org]
Therefore opsi-winst installs as 32 bit program scripts, that run on 32 bit system fine, on 64 bit system correct without any change.
For the installation of 64 bit programs some constants like %ProgramFilesDir% returns the wrong values. Therefore we have since opsi-winst 4.10.8 some new features:
Normally you may (and should) tell explicit to which place you want to write or from where you want to read. Here we have three variants:
Following this idea, we have some additional constants:
Table 9.1. Constants
| Constant | 32 Bit | 64 Bit |
|---|---|---|
| c:\program files | c:\program files (x86) |
| c:\program files | c:\program files (x86) |
| c:\program files | c:\program files |
| c:\program files | c:\program files |
%ProgramFilesDir%
%ProgramFiles32Dir%
%ProgramFiles64Dir%
%ProgramFilesSysnativeDir%
For a reading access to the different aereas of registry and filesystem we have now the following new functions:
GetRegistrystringvalue32
GetRegistrystringvalue64
GetRegistrystringvalueSysNative
FileExists32
FileExists64
FileExistsSysNative
A simple call to Registry-section results in writing to the 32 bit registry regions. Also a simple call to Files-section results in writing to the 32 bit file system regions.
For Registry and Files section we have now the additional calling options:
/32Bit/64Bit/SysNativeIn addition to these opsi-winst functions, we copy at the installation of the opsi-client agent the (64 bit) file c:\windows\system32\cmd.exe to c:\windows\cmd64.exe. Using this cmd64.exe with ExecWith sections you may call any 64 bit operations on the command line.
Examples:
File handling:
if $INST_SystemType$ = "64 Bit System"
comment ""
comment "-------------------------------------"
comment "Testing: "
message "64 Bit redirection"
Files_copy_test_to_system32
if FileExists("%System%\dummy.txt")
comment "passed"
else
LogWarning "failed"
set $TestResult$ = "not o.k."
endif
ExecWith_remove_test_from_system32 'cmd.exe' /C
Files_copy_test_to_system32 /64Bit
if FileExists64("%System%\dummy.txt")
comment "passed"
else
LogWarning "failed"
set $TestResult$ = "not o.k."
endif
ExecWith_remove_test_from_system32 '%SystemRoot%\cmd64.exe' /C
endifRegistry Handling:
message "Write to 64 Bit Registry"
if ($INST_SystemType$ = "64 Bit System")
set $ConstTest$ = ""
set $regWriteValue$ = "64"
set $CompValue$ = $regWriteValue$
Registry_opsi_org_test /64Bit
ExecWith_opsi_org_test "%systemroot%\cmd64.exe" /c
set $ConstTest$ = GetRegistryStringValue64("[HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\test] bitByWinst")
if ($ConstTest$ = $CompValue$)
comment "passed"
else
set $TestResult$ = "not o.k."
comment "failed"
endif
set $ConstTest$ = GetRegistryStringValue64("[HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\test] bitByReg")
if ($ConstTest$ = $CompValue$)
comment "passed"
else
set $TestResult$ = "not o.k."
comment "failed"
endif
set $regWriteValue$ = "32"
set $CompValue$ = $regWriteValue$
Registry_opsi_org_test
ExecWith_opsi_org_test "cmd.exe" /c
set $ConstTest$ = GetRegistryStringValue("[HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\test] bitByWinst")
if ($ConstTest$ = $CompValue$)
comment "passed"
else
set $TestResult$ = "not o.k."
comment "failed"
endif
set $ConstTest$ = GetRegistryStringValue("[HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\test] bitByReg")
if ($ConstTest$ = $CompValue$)
comment "passed"
else
set $TestResult$ = "not o.k."
comment "failed"
endif
else
set $regWriteValue$ = "32"
set $CompValue$ = $regWriteValue$
Registry_opsi_org_test /64Bit
ExecWith_opsi_org_test "cmd.exe" /c
set $ConstTest$ = GetRegistryStringValue64("[HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\test] bitByWinst")
if ($ConstTest$ = $CompValue$)
comment "passed"
else
set $TestResult$ = "not o.k."
comment "failed"
endif
set $ConstTest$ = GetRegistryStringValue64("[HKEY_LOCAL_MACHINE\SOFTWARE\opsi.org\test] bitByReg")
if ($ConstTest$ = $CompValue$)
comment "passed"
else
set $TestResult$ = "not o.k."
comment "failed"
endif
endif
if ($INST_SystemType$ = "64 Bit System")
set $regWriteValue$ = "64"
Registry_hkcu_opsi_org_test /AllNtUserDats /64Bit
set $regWriteValue$ = "32"
Registry_hkcu_opsi_org_test /AllNtUserDats
else
set $regWriteValue$ = "32"
Registry_hkcu_opsi_org_test /AllNtUserDats
Registry_hkcu_opsi_org_test /AllNtUserDats /64Bit
endifThis chapter contains a growing collection of examples showing real world problems that can be mastered by simple or sophisticated pieces opsi-winst scripting.
Since opsi-winst 4.2 there is an easy solution for this task: To remove a file alt.txt from all subdirectories of the user profile directory the following Files call can be used:
files_delete_Alt /allNtUserProfiles [files_delete_Alt] delete "%UserProfileDir%\alt.txt"
Neverthelesse we document a workaround which could be used in older opsi-winst versions. It demonstrates some techniques which may be helpful for other purposes.
The following ingredients are needed:
The complete script should look like:
[Actions]
; variable for file name
DefVar $deleteFile$
set $deleteFile$ = "alt.txt"
; String list declarations
DefStringList list0
DefStringList list1
; capture the lines produced by the dos dir command
Set list0 = getOutStreamFromSection ('dosbatch_profiledir')
; Loop through the lines. Call a files section for each line.
for $x$ in list0 do files_delete_x
; Here are the two special sections
[dosbatch_profiledir]
@dir "%ProfileDir%" /b
[files_delete_x]
delete "%ProfileDir%\$x$\$deleteFile$"If we want to check if a specific service (exemplified with "opsiclientd") is running, and, e.g., if it is not running, start it, we may use the following script.
In order to get the list of running services we launch the command
net start
in a DosBatch section, capturing its output in list0. We trim the list, and iterate through its elements, thus seeing if the specified service is in it. If not, we do something for it.
[Actions]
DefStringList $list0$
DefStringList $list1$
DefStringList $result$
Set $list0$=getOutStreamFromSection('DosBatch_netcall')
Set $list1$=getSublist(2:-3, $list0$)
DefVar $myservice$
DefVar $compareS$
DefVar $splitS$
DefVar $found$
Set $found$ ="false"
set $myservice$ = "opsiclientd"
comment "============================"
comment "search the list"
; for developping loglevel = 7
; setloglevel=7
; in normal use we dont want to log the looping
setloglevel = 5
for %s% in $list1$ do sub_find_myservice
setloglevel=7
comment "============================"
if $found$ = "false"
set $result$ = getOutStreamFromSection ("dosinanicon_start_myservice")
endif
[sub_find_myservice]
set $splitS$ = takeString (1, splitStringOnWhiteSpace("%s%"))
Set $compareS$ = $splitS$ + takeString(1, splitString("%s%", $splitS$))
if $compareS$ = $myservice$
set $found$ = "true"
endif
[dosinanicon_start_myservice]
net start "$myservice$"
[dosbatch_netcall]
@echo off
net startSometimes it is necessary to run an installation script as an ordinary local user and not in the context of the opsi service. For example, there are installations that require a user context or use other services that are started after a user login.
MSI installations which seem to need a local user can sometimes be configured by the option ALLUSERS=2 to proceed without such a user:
[Actions] DefVar $LOG_LOCATION$ Set $LOG_LOCATION$ = "c:\tmp\myproduct.log" winbatch_install_myproduct [winbatch_install_myproduct] msiexec /qb ALLUSERS=2 /l* $LOG_LOCATION$ /i %SCRIPTPATH%\files\myproduct.msi
In other case it is necessary to create a temporary administrative user in whose context the installation takes place. This can be done as follows:
ExitWindows /Reboot
[Actions] Registry_del_autologin ;.... [Registry_del_autologin] openkey [HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] set "DefaultUserName"="" set "DefaultPassword"=""
The opsi-winst script template temporarily generates a user context, executes an installation in it, then removes it. Before using the template the following values are to be set adequately:
The script proceeds as follows:
We call the two involved opsi-winst scripts master script and local script . The first one runs in a system service context, the second which does the specific software installation runs in the context of a local administrator.
If the local script requires internal reboots then the master script must be adapted to produce them. As long as the local script is not finished the master script hands over control to the local script by an ExitWindows /ImmediateLogout. Of course the RunOnce entry has to be created for each run. Since username and password for the autologon are removed at the beginning of the local script they have to be reset each time as well.
There is no direct access from the local script to the product properties (usually via the String function GetProductProperty) . If there are values needed the master script must retrieve them and e.g. save them temporarily in the registry.
There may be product installations by external setup program calls which change registry entries which are saved by the master script and usually written back at the end of the installation. In this case the master script must be adapted to avoid writing back.
The local script runs with an administrator logged in. You have to lock the keyboard when testing is done. Otherwise anybody sitting at the client could stop script execution and take over the session.
In the following example, the password of the tempory opsiSetupAdmin user is set via the function RandomStr, which is strongly recommended.
In order to avoid logging of passwords the loglevel is temporarily set to -2.
Please do not use the script as printed below, but use the opsi product: opsi-template-with-admin.
; Copyright (c) uib gmbh (www.uib.de)
; This sourcecode is owned by uib
; and published under the Terms of the General Public License.
; TEMPLATE for
; Skript fuer Installationen im Kontext eines temporaeren lokalen Administrators
; installations as temporary local admin
; see winst_manual.pdf / winst_handbuch.pdf
; !!! requires winst32.exe version 4.2.x !!!
;
; !!! Das lokale Installations-Skript, das durch den temporaeren lokalen Admin ausgefuehrt wird
; !!! (sein Name steht in $LocalSetupScript$), muss mit dem Befehl
; !!! exitWindows /Reboot
; !!! enden
;
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; Vorarbeiten/Voraussetzungen/Doku pruefen wie in Winsthandbuch
; 8.3 Skript fuer Installationen im Kontext eines lokalen Administrators
; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
[Actions]
requiredWinstVersion >= 4.10.6
setLogLevel=7
DefVar $ProductName$
DefVar $ProductSizeMB$
DefVar $LocalSetupScript$
DefVar $LockKeyboard$
DefVar $OpsiAdminPass$
DefVar $OS$
DefVar $MinorOS$
DefVar $MsVersion$
DefVar $RebootFlag$
DefVar $ShutdownRequested$
DefVar $WinstRegKey$
DefVar $RebootRegVar$
DefVar $AutoName$
DefVar $AutoPass$
DefVar $AutoDom$
DefVar $AutoLogon$
DefVar $AutoBackupKey$
DefVar $LocalFilesPath$
DefVar $LocalWinst$
DefVar $SystemType$
DefVar $platform_cmdexe$
DefVar $DefaultLoglevel$
DefVar $PasswdLogLevel$
DefVar $AdminGroup$
DefVar $SearchResult$
DefStringlist $ResultList$
DefStringlist $ResultList2$
DefStringlist $ResultList3$
comment "set $LockKeyboard$ to true to prevent user hacks while admin is logged in"
Set $LockKeyboard$="true"
Set $LockKeyboard$="false"
Set $ProductName$ = "opsi-template-with-admin"
Set $ProductSizeMB$ = "1"
Set $LocalSetupScript$ = "local_setup.ins"
set $OS$ = GetOS
set $MinorOS$ = GetNTVersion
Set $SystemType$ = GetSystemType
Set $MsVersion$ = GetMsVersionInfo
set $DefaultLoglevel$ = "7"
comment " set $PasswdLogLevel$ to 0 for production"
Set $PasswdLogLevel$="7"
SetLogLevel=$DefaultLoglevel$
if not (fileExists ("%Scriptpath%\psgetsid.exe"))
LogError "psgetsid.exe is missing. Please install it from http://download.sysinternals.com/Files/PsTools.zip to %Scriptpath%"
isFatalError
endif
if not(($SystemType$ = "64 Bit System") or ($SystemType$ = "x86 System"))
LogError "Unknown Systemtype: "+$SystemType$+" - aborting"
isFatalError
endif
if $SystemType$ = "64 Bit System"
set $platform_cmdexe$ = "%SystemRoot%\cmd64.exe"
else
set $platform_cmdexe$ = "%System%\cmd.exe"
endif
comment "handle Rebootflag"
Set $WinstRegKey$ = "HKLM\SOFTWARE\opsi.org\winst"
Set $RebootFlag$ = GetRegistryStringValue("["+$WinstRegKey$+"] "+"RebootFlag")
Set $ShutdownRequested$ = GetRegistryStringValue("["+$WinstRegKey$+"] "+"ShutdownRequested")
sub_test_autologon_data
comment "some paths required"
Set $AutoBackupKey$ = $WinstRegKey$+"\AutoLogonBackup"
Set $LocalFilesPath$ = "C:\opsi_local_inst"
Set $LocalWinst$ = "c:\opsi\utils\winst32.exe"
if not( FileExists($LocalWinst$) )
Set $LocalWinst$ = "%ProgramFilesDir%\opsi.org\preloginloader\utils\winst32.exe"
endif
if not( FileExists($LocalWinst$) )
Set $LocalWinst$ = "%ProgramFilesDir%\opsi.org\preloginloader\opsi-winst\winst32.exe"
endif
if not( FileExists($LocalWinst$) )
Set $LocalWinst$ = "%ProgramFilesDir%\opsi.org\opsi-client-agent\opsi-winst\winst32.exe"
endif
if not( FileExists($LocalWinst$) )
LogError "No opsi-winst found. Abborting."
isFatalError
endif
comment "show product picture"
ShowBitmap /3 "%scriptpath%\localsetup\"+$ProductName$+".png" $ProductName$
if not (($RebootFlag$ = "1") or ($RebootFlag$ = "2") or ($RebootFlag$ = "3"))
comment "Part before first Reboot"
comment "just reboot - this must be done if this is the first product after OS installation"
comment "handle Rebootflag"
Set $RebootFlag$ = "1"
Registry_SaveRebootFlag
ExitWindows /ImmediateReboot
endif ; Rebootflag = not (1 or 2 or 3)
if $RebootFlag$ = "1"
comment "Part before second Reboot"
if not(HasMinimumSpace ("%SYSTEMDRIVE%", ""+$ProductSizeMB$+" MB"))
LogError "Not enough space on drive C: . "+$ProductSizeMB$+" MB on C: required for "+$ProductName$
isFatalError
endif
comment "Lets work..."
Message "Preparing "+$ProductName$+" install step 1..."
sub_Prepare_AutoLogon
comment "we need to reboot now to be sure that the autologon work"
comment "handle Rebootflag"
Set $RebootFlag$ = "2"
Registry_SaveRebootFlag
sub_test_autologon_data
ExitWindows /ImmediateReboot
endif ; Rebootflag = not (1 or 2)
if ($RebootFlag$ = "2")
comment "Part after first Reboot"
comment "handle Rebootflag"
Set $RebootFlag$ = "3"
Registry_SaveRebootFlag
comment "Lets work..."
Message "Preparing "+$ProductName$+" install step 2..."
Registry_enable_keyboard /64bit
comment "now let the autologon work"
comment "it will stop with a reboot"
ExitWindows /ImmediateLogout
endif ; Rebootflag = 2
if ($RebootFlag$ = "3")
comment "Part after second Reboot"
comment "handle Rebootflag"
Set $RebootFlag$ = "0"
Registry_SaveRebootFlag
comment "Lets work..."
Message "Cleanup "+$ProductName$+" install (step 3)..."
sub_Restore_AutoLogon
set $SearchResult$ = GetRegistryStringValue64("[HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce] opsi_autologon_setup")
if $SearchResult$ = $LocalWinst$+" "+$LocalFilesPath$+"\"+$LocalSetupScript$+" /batch"
LogError "Localscript did not run. We remove the RunOnce entry and abort"
Registry_del_runonce /64Bit
isFatalError
endif
comment "This is the clean end of the installation"
endif ; Rebootflag = 3
ExitWindows /Reboot
[sub_Prepare_AutoLogon]
comment "copy the setup script and files"
Files_copy_Setup_files_local
comment "read actual Autologon values for backup"
set $AutoName$ = GetRegistryStringValue64("[HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] DefaultUserName")
comment "if AutoLogonName is our setup admin user, something bad happend"
comment "then let us cleanup"
if ($AutoName$="opsiSetupAdmin")
set $AutoName$=""
set $AutoPass$=""
set $AutoDom$=""
set $AutoLogon$="0"
else
set $AutoPass$ = GetRegistryStringValue64("[HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] DefaultPassword")
set $AutoDom$ = GetRegistryStringValue64("[HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] DefaultDomainName")
set $AutoLogon$ = GetRegistryStringValue64("[HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] AutoAdminLogon")
endif
comment "backup AutoLogon values"
Registry_save_autologon
comment "prepare the admin AutoLogon"
SetLogLevel=$PasswdLogLevel$
set $OpsiAdminPass$= randomstr
Registry_autologon /64Bit
comment "get the name of the admin group"
comment "using psgetsid from sysinernals pstools"
set $ResultList$ = getOutStreamFromSection("DosInAnIcon_get_admin_group")
set $AdminGroup$ = takeString(6,$ResultList$)
set $AdminGroup$ = takeString(1,splitstring($AdminGroup$,"\"))
comment "create our setup admin user"
DosInAnIcon_makeadmin
SetLogLevel=$DefaultLoglevel$
comment "remove c:\tmp\winst.bat with password"
Files_remove_winst_bat
comment "store our setup script as run once"
Registry_runOnce /64Bit
comment "disable keyboard and mouse while the autologin admin works"
if ($LockKeyboard$="true")
Registry_disable_keyboard /64Bit
endif
[sub_test_autologon_data]
set $AutoPass$ = GetRegistryStringValue64("[HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] DefaultPassword")
set $AutoDom$ = GetRegistryStringValue64("[HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] DefaultDomainName")
set $AutoLogon$ = GetRegistryStringValue64("[HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon] AutoAdminLogon")
[sub_Restore_AutoLogon]
comment "read AutoLogon values from backup"
set $AutoName$ = GetRegistryStringValue("["+$AutoBackupKey$+"] DefaultUserName")
set $AutoPass$ = GetRegistryStringValue("["+$AutoBackupKey$+"] DefaultPassword")
set $AutoDom$= GetRegistryStringValue("["+$AutoBackupKey$+"] DefaultDomainName")
set $AutoLogon$= GetRegistryStringValue("["+$AutoBackupKey$+"] AutoAdminLogon")
comment "restore the values"
SetLogLevel=$PasswdLogLevel$
Registry_restore_autologon /64Bit
SetLogLevel=$DefaultLoglevel$
comment "delete our setup admin user"
DosInAnIcon_deleteadmin
comment "cleanup setup script, files and profiledir"
Files_delete_Setup_files_local
comment "delete profiledir"
DosInAnIcon_deleteprofile
[Registry_save_autologon]
openkey [$AutoBackupKey$]
set "DefaultUserName"="$AutoName$"
set "DefaultPassword"="$AutoPass$"
set "DefaultDomainName"="$AutoDom$"
set "AutoAdminLogon"="$AutoLogon$"
[Registry_restore_autologon]
openkey [HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]
set "DefaultUserName"="$AutoName$"
set "DefaultPassword"="$AutoPass$"
set "DefaultDomainName"="$AutoDom$"
set "AutoAdminLogon"="$AutoLogon$"
[DosInAnIcon_deleteadmin]
NET USER opsiSetupAdmin /DELETE
[Registry_SaveRebootFlag]
openKey [$WinstRegKey$]
set "RebootFlag" = "$RebootFlag$"
[Files_copy_Setup_files_local]
copy -s %ScriptPath%\localsetup\*.* $LocalFilesPath$
[Files_delete_Setup_files_local]
delete -sf $LocalFilesPath$
; folgender Befehl funktioniert nicht vollständig, deshalb ist er zur Zeit auskommentier
; der Befehl wird durch die Sektion "DosInAnIcon_deleteprofile" ersetzt (P.Ohler)
;delete -sf "%ProfileDir%\opsiSetupAdmin"
[DosInAnIcon_deleteprofile]
rmdir /S /Q "%ProfileDir%\opsiSetupAdmin"
[DosInAnIcon_get_admin_group]
@echo off
"%ScriptPath%\psgetsid.exe" /accepteula S-1-5-32-544
[DosInAnIcon_makeadmin]
NET USER opsiSetupAdmin $OpsiAdminPass$ /ADD
NET LOCALGROUP $AdminGroup$ /ADD opsiSetupAdmin
[Registry_autologon]
openkey [HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]
set "DefaultUserName"="opsiSetupAdmin"
set "DefaultPassword"="$OpsiAdminPass$"
set "DefaultDomainName"="localhost"
set "AutoAdminLogon"="1"
[Registry_runonce]
openkey [HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce]
set "opsi_autologon_setup"='"$LocalWinst$" "$LocalFilesPath$\$LocalSetupScript$" /batch'
[Registry_del_runonce]
openkey [HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce]
DeleteVar "opsi_autologon_setup"
[Registry_disable_keyboard]
openkey [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kbdclass]
set "Start"=REG_DWORD:0x4
openkey [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Mouclass]
set "Start"=REG_DWORD:0x4
[Registry_enable_keyboard]
openkey [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kbdclass]
set "Start"=REG_DWORD:0x1
openkey [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Mouclass]
set "Start"=REG_DWORD:0x1
[Files_remove_winst_bat]
delete -f c:\tmp\_winst.batSetting the template path can be done by the following script extracts
[Actions]
; ....
DefVar $oooTemplateDirectory$
;----------------------------------------------------------------------
;set path here:
Set $oooTemplateDirectory$ = "file://server/share/verzeichnis"
;----------------------------------------------------------------------
;...
DefVar $sofficePath$
Set $sofficePath$= GetRegistryStringValue ("[HKEY_LOCAL_MACHINE\SOFTWARE\OpenOffice.org\OpenOffice.org\2.0] Path")
DefVar $oooDirectory$
Set $oooDirectory$= SubstringBefore ($sofficePath$, "\program\soffice.exe")
DefVar $oooShareDirectory$
Set $oooShareDirectory$ = $oooDirectory$ + "\share"
XMLPatch_paths_xcu $oooShareDirectory$+"\registry\data\org\openoffice\Office\Paths.xcu"
; ...
[XMLPatch_paths_xcu]
OpenNodeSet
- error_when_no_node_existing false
- warning_when_no_node_existing true
- error_when_nodecount_greater_1 false
- warning_when_nodecount_greater_1 true
- create_when_node_not_existing true
- attributes_strict false
documentroot
all_childelements_with:
elementname: "node"
attribute:"oor:name" value="Paths"
all_childelements_with:
elementname: "node"
attribute: "oor:name" value="Template"
all_childelements_with:
elementname: "node"
attribute: "oor:name" value="InternalPaths"
all_childelements_with:
elementname: "node"
end
SetAttribute "oor:name" value="$oooTemplateDirectory$"As treated in Section 10.4, “XML File Patching: Setting Template Path for OpenOffice.org 2” , opsi-winst can evaluate and modify XML files.
An example shall demonstrate how a value can be retrieved from a XML file. We assume that the following XML file is:
<?xml version="1.0" encoding="utf-16" ?>
<Collector xmlns="http://schemas.microsoft.com/appx/2004/04/Collector" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="Collector.xsd" UtcDate="04/06/2006 12:28:17" LogId="{693B0A32-76A2-4FA0-979C-611DEE852C2C}" Version="4.1.3790.1641" >
<Options>
<Department></Department>
<IniPath></IniPath>
<CustomValues>
</CustomValues>
</Options>
<SystemList>
<ChassisInfo Vendor="Chassis Manufacture" AssetTag="System Enclosure 0" SerialNumber="EVAL"/>
<DirectxInfo Major="9" Minor="0"/>
</SystemList>
<SoftwareList>
<Application Name="Windows XP-Hotfix - KB873333" ComponentType="Hotfix" EvidenceId="256" RootDirPath="C:\WINDOWS\$NtUninstallKB873333$\spuninst" OsComponent="true" Vendor="Microsoft Corporation" Crc32="0x4235b909">
<Evidence>
<AddRemoveProgram DisplayName="Windows XP-Hotfix - KB873333" CompanyName="Microsoft Corporation" Path="C:\WINDOWS\$NtUninstallKB873333$\spuninst" RegistryPath="HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\KB873333" UninstallString="C:\WINDOWS\$NtUninstallKB873333$\spuninst\spuninst.exe" OsComponent="true" UniqueId="256"/>
</Evidence>
</Application>
<Application Name="Windows XP-Hotfix - KB873339" ComponentType="Hotfix" EvidenceId="257" RootDirPath="C:\WINDOWS\$NtUninstallKB873339$\spuninst" OsComponent="true" Vendor="Microsoft Corporation" Crc32="0x9c550c9c">
<Evidence>
<AddRemoveProgram DisplayName="Windows XP-Hotfix - KB873339" CompanyName="Microsoft Corporation" Path="C:\WINDOWS\$NtUninstallKB873339$\spuninst" RegistryPath="HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\KB873339" UninstallString="C:\WINDOWS\$NtUninstallKB873339$\spuninst\spuninst.exe" OsComponent="true" UniqueId="257"/>
</Evidence>
</Application>
</SoftwareList>
</Collector>To read the elements and get the values of all „Application“ nodes we may use these extracts of code:
[Actions]
DefStringList $list$
...
set $list$ = getReturnListFromSection ('XMLPatch_findProducts '+$TEMP$+'\test.xml')
for $line$ in $list$ do Sub_doSomething
[XMLPatch_findProducts]
openNodeSet
; Node „Collector“ is documentroot
documentroot
all_childelements_with:
elementname:"SoftwareList"
all_childelements_with:
elementname:"Application"
end
return elements
[Sub_doSomething]
set $escLine$ = EscapeString:$line$
; now we can work on the content of $escLine$We encapsulate the retrieved Strings by setting their values as a whole into an variable via an EscapeString call. Since the loop variable %line% is not a common variable but behaves like a constant all special characters in it (as < > $ % “ ') may cause difficulties.
The opsi-winst XMLPatch section requires fully declared XML name spaces (as is postulated in the XML RFC). But there are XML configuration files which do not declare „obvious“ elements (and the interpreting programs insist that the file looks this way). Especially patching the lots of XML/XCU configuration files of OpenOffice.org proved to be a hard job. For solving this task, A. Pohl (many thanks!) the functions XMLaddNamespace and XMLremoveNamespace. Its usage is demonstrated by the following example:
DefVar $XMLFile$
DefVar $XMLElement$
DefVar $XMLNameSpace$
set $XMLFile$ = "D:\Entwicklung\OPSI\winst\Common.xcu3"
set $XMLElement$ = 'oor:component-data'
set $XMLNameSpace$ = 'xmlns:xml="http://www.w3.org/XML/1998/namespace"'
if XMLAddNamespace($XMLFile$,$XMLElement$, $XMLNameSpace$)
set $NSMustRemove$="1"
endif
;
; now the XML Patch should work
; (commented out since not integrated in this example)
;
; XMLPatch_Common $XMLFile$
;
; when finished we rebuild the original format
if $NSMustRemove$="1"
if not (XMLRemoveNamespace($XMLFile$,$XMLElement$,$XMLNameSpace$))
LogError "XML-Datei konnte nicht korrekt wiederhergestellt werden"
isFatalError
endif
endifPlease observe that the XML file must be formatted such that the element tags do not contain line breaks.
The information which is shown additionally may give a hint to the problem: