What Are Variables?
Variables in Xi-Batch are named values the scheduler holds in shared memory. Users and jobs read and change them, and the scheduler consults them when deciding whether a job may start. They are used for:
- Job dependency control - a job waits until a condition on a variable is met
- State tracking across jobs
- Passing a small amount of data between jobs
- Workflow coordination, including between networked hosts
- A small set of scheduler settings the product maintains as variables (LOADLEVEL, CLOAD, LOGJOBS, LOGVARS, MACHINE)
This article covers the variables themselves and the two commands that manage them. How a job waits on a variable and how a job changes one are the subject of a separate article, Controlling Xi-Batch Job Flow with Conditions and Assignments. The job log and the variable log have their own articles (LOGJOBS and LOGVARS), and what happens to an exported variable when a host goes away is covered by Diagnosing Xi-Batch Variables That Do Not Reach Another Host.
Every command here needs a running scheduler. Without one, btvar stops with "Scheduler not running".
Variable Components
Each variable carries:
- Name
- 1 to 19 characters. The first must be a letter; the rest may be letters, digits or underscores
- A longer name is silently truncated to 19 characters
- Case-sensitive, and unique on the machine that owns it
- Examples: STATUS, Counter1, Backup_Done
- Value
- Either a whole number or a text string
- Whole number: a 32-bit signed integer, -2147483648 to 2147483647
- Text: up to 49 characters
- Comment
- Free text, up to 41 characters
- Never read by the scheduler - documentation only
- User and group
- The owner and owning group, initially the creator's
- Both can be transferred; both take part in the permission test
- Mode
- Ten permission bits, held separately for user, group and other
- Described under Variable Permissions below
- Export state
- One of three: Local only, Exported, or Cluster
- Cluster implies exported; the two flags cannot be set independently
Whole Numbers and Text
A variable has no declared type. Its type is decided by the value that is stored in it, and it changes whenever the value changes.
A value made up entirely of digits, optionally with a leading minus sign, is stored as a whole number. Anything else - including a value with a leading plus sign, a decimal point, a space or a letter - is stored as text.
btvar -C -s 123 value # whole number 123
btvar -C -s -456 value # whole number -456
btvar -C -s abc value # text "abc"
btvar -C -s 12.5 value # text "12.5" - the dot makes it text
To store a value that looks like a number as text, add -S (force string):
btvar -S -s 00123 partno # text "00123", leading zeros kept
btvlist shows a text value inside double quotes when it contains a space or begins with a digit. The quotes are part of the display, not part of the value.
Arithmetic is not part of btvar. btvar -s '+=5' counter does not add five to anything - it replaces the value with the four-character text +=5, silently. There are two real ways to do arithmetic on a variable:
- In btq, on the variables screen: press = to set the arithmetic constant, then +, -, *, / or % to apply it
- As a job assignment - btr -s 'counter+=1' - which is the subject of the conditions and assignments article
System Variables
Five system variables exist. The scheduler creates them the first time it starts and re-creates any that are missing at every start.
- LOADLEVEL
- Maximum total load level for running jobs
- A job starts only if CLOAD + the job's load level does not exceed it
- Accepts a whole number only
- CLOAD (read-only)
- Current total load level of running jobs
- Maintained by the scheduler; an attempt to set it is refused with "Attempting to reset a read-only system variable"
- LOGJOBS
- Destination for the job audit log - a file name or a | command
- Accepts text only; an empty value disables logging
- LOGVARS
- Destination for the variable audit log, on the same terms
- Accepts text only
- MACHINE (effectively read-only)
- This host's name, refreshed at every scheduler start
- Present only when networking is licensed
- Accepts text only
Setting a system variable to the wrong type is refused with "Attempting to set system variable name to wrong type", and no system variable can be deleted - "You cannot delete a system variable such as name".
The names above are the shipped defaults. They are read from the product's message file at scheduler start, so a site that has had them renamed will see different names; what identifies a system variable is its behaviour, not its spelling.
STARTLIM and STARTWAIT are not variables. Older documentation presents them as system variables that btvar can query and change. On this release they are settings in the master configuration file /etc/xi/batchconfig, the scheduler reads them at start-up, and no command reports the values in use. See Controlling the Xi-Batch Job Start Rate with STARTLIM and STARTWAIT.
Listing the system variables:
btvlist -N -F '%N %V' LOADLEVEL CLOAD LOGJOBS LOGVARS MACHINE
Local, Exported and Cluster Variables
Every variable has exactly one owning host. There are three export states.
| State | Visible to other hosts | Usable in another host's job | Set with |
|---|---|---|---|
| Local only | No | No | btvar -L NAME |
| Exported | Yes | Yes - one shared value | btvar -E -k NAME |
| Cluster | Yes | Yes - each host resolves it to its own copy | btvar -E -K NAME |
A cluster variable must also be exported; setting the cluster flag on an unexported variable is refused. A cluster variable cannot be renamed, and its name must be unique on the machine.
An exported variable is not replicated by a synchronisation protocol. The owning host applies each change and, in the same operation, writes it to the hosts it is connected to at that moment; a host that is not connected receives nothing and asks for nothing, and picks the value up when a connection is next made. A replica can never be written locally - a change against another host's variable is forwarded to the owner, and the export state itself can only be changed on the owning host. The consequences of that model, and what happens when a host disappears, are covered in Diagnosing Xi-Batch Variables That Do Not Reach Another Host.
Referring to another host's variable: prefix the name with the host name and a colon.
# Read the local copy of server1's SHARED_STATUS
btvar server1:SHARED_STATUS
That reads this machine's replica. It does not contact server1, and it cannot report a connection failure. To see the master value, read it on the owning host.
Variable Permissions
A variable carries ten permission bits, held separately for the owner, the owning group and everyone else:
- Show - the variable's existence and name are visible
- Read - the value can be read (implies Show)
- Write - the value can be changed (implies Read and Show)
- Read mode - the permissions can be examined
- Write mode - the permissions can be changed (implies Read mode)
- Give away user and Assume user - transfer of ownership, from each side
- Give away group and Assume group - transfer of the owning group, from each side
- Delete - the variable can be deleted or renamed
Reading the permissions:
btvlist -H -F '%N %U %G %M' STATUS
Changing them from the command line with -M. The argument is one or more clauses separated by commas, each written as an optional u, g and o selector followed by a colon, then =, + or -, then the permission letters. With no selector the clause applies to all three.
The permission letters must be upper case. A lower-case letter is rejected with "Bad mode string". They are R read, W write, S show, M read mode, P write mode, U give away user, V assume user, G give away group, H assume group, D delete. The u, g and o selector before the colon may be either case.
# Owner may do anything; group and others may read only
btvar -M 'U:=RWMPD,GO:=R' STATUS
# Take write permission away from others, leave the rest alone
btvar -M 'O:-W' STATUS
Changing them in btq: on the variables screen, move to the variable and press M. In the mode editor, the arrow keys (or h and l) move between the boxes, y or t sets a permission, n or f clears it, and ~ or ! toggles it.
A mode that would leave nobody able to do anything is refused: "There are too few modes permitted on this variable for anyone to do anything with it".
The btvar Command
btvar reads, sets, creates, deletes and re-permissions a single variable. Its options are:
| Option | Long form | Effect |
|---|---|---|
| -s value | --set-value | Set the value. The value is the option's argument; the variable name is the last argument on the line. |
| -C | --create | Create the variable if it does not exist. Only meaningful with -s |
| -D | --delete | Delete the variable |
| -S | --force-string | Store the value as text even if it looks like a number |
| -c text | --comment | Set the comment. Takes effect only alongside -s |
| -u value | --undefined-value | Value to print or compare with if the variable does not exist |
| -M modes | --set-mode | Change the permissions |
| -U user | --set-owner | Change the owner |
| -G group | --set-group | Change the owning group |
| -E / -L / -N | --set-export / --set-local / --reset-export | Export, make local, or leave the export state alone |
| -K / -k / -o | --cluster / --no-cluster / --reset-cluster | Set, clear, or leave alone the cluster flag |
| -X | --cancel | Cancel the create, delete, set and force-string flags set so far |
| -eq -ne -lt -le -gt -ge | Compare - see Testing a Variable in a Script | |
| -? | --explain | Print the usage summary |
There is no -v, no -d and no -A. An unrecognised letter is rejected with a usage error, and a letter that means something else - -c for create, -u for owner, -g for group - is accepted and does something different.
Reading a Variable
With no operation option, btvar prints the value on one line and exits.
btvar STATUS
btvar server1:SHARED_STATUS
If the variable does not exist the command prints an error and exits with status 20. Supply a fallback with -u to get a value and status 0 instead:
STATUS=$(btvar -u Unknown STATUS)
The output is the bare value and nothing else - no name, no label and no export status. Export status comes from btvlist.
Creating Variables
Creation is -C together with -s: the variable is created only if it is absent, and set either way.
# Create a counter starting at zero
btvar -C -s 0 counter
# Create a text variable with a comment
btvar -C -s Pending -c "Nightly backup state" backup_status
# Create an exported variable in one step
btvar -C -s 0 -E -k total_processed
In btq:
- Press V on the job list to switch to the variables screen (or start with btq -v)
- Press C to create
- Enter the name, then the value
- Press " on the new variable to add a comment
Modifying Variables
Change the value:
btvar -s Ready status
btvar -s 105 counter
Without -C, setting a variable that does not exist fails with "Variable does not exist" and status 20 rather than creating it.
Change the comment. A comment can only be changed alongside a value. A btvar line carrying -c and nothing else prints the variable's value and changes nothing - it is not an error, and nothing is reported.
# Re-state the current value to change the comment with it
btvar -s "$(btvar backup_status)" -c "New description" backup_status
In btq, press " on the variable to change the comment on its own.
Change ownership:
btvar -U newuser varname
btvar -G newgroup varname
Both need the matching give-away and assume permissions, or the write-admin privilege.
Change the export state:
btvar -E -k shared_status # exported, not clustered
btvar -E -K shared_status # exported and clustered
btvar -L shared_status # back to local only
Changing the export state of a variable that a job refers to in a condition or an assignment is refused with "One or more jobs refer to this variable either in conditions or assignments".
Deleting Variables
btvar -D varname
In btq, move to the variable and press D.
Four things refuse a delete, each with its own message:
- A system variable - "You cannot delete a system variable such as name"
- A variable a job refers to - "One or more jobs refer to this variable either in conditions or assignments"
- Another host's variable - it must be deleted on the host that owns it
- Lack of the delete permission
Listing Variables
btvlist lists variables. Its options are -L (local host only), -R (include other hosts, the default), -F format, -D (default format), -H (print a column heading), -N (no heading), -u user, -g group, -B (bypass modes) and -?. There is no -e and no -v; names given after the options select which variables to show.
All variables, default format:
btvlist
The default format is %N %V %E # %C - name, value, the word Export if the variable is exported, then the comment. Other hosts' variables appear with a host: prefix on the name.
Exported variables, and which of them are clustered:
btvlist -H -F '%N %V %E %K'
%K prints the word Cluster. It is absent from the default format, so a plain btvlist cannot distinguish an exported variable from a clustered one.
Specific variables:
btvlist counter backup_status
With owner, group and permissions:
btvlist -H -F '%N %U %G %M %V'
Only this host's variables:
btvlist -L
Another host's variables, as that host sees them:
ssh server1 btvlist -L
Testing a Variable in a Script
btvar can compare a variable with a value and report the answer as its exit status, which avoids parsing output. The comparison word comes before the value, and the variable name is last.
if btvar -eq Complete backup_status
then
echo "backup finished"
fi
# Numeric comparison, with a fallback if the variable is absent
btvar -u 0 -ge 100 processed_count && run-summary.sh
The words are -eq, -ne, -lt, -le, -gt and -ge, and each may also be written with a leading + or --. The single letters -e, -n, -l and -g are short forms of -eq, -ne, -lt and -gt. Exit status is 0 if the comparison holds, 1 if it does not, and 20 if the variable does not exist and no -u was given.
The comparison must be the last option on the line. Option processing stops at it, so anything after it is read as the value to compare with and then the variable name. Put -u, -S and any other option before the comparison, never after.
The comparison is numeric when both sides are whole numbers and lexical otherwise.
Testing and Setting in One Step
A comparison can be combined with -s, and when it is, the test and the assignment are atomic: the value is written only if the variable has not been changed by anyone else since btvar read it. This is the one place in the product where a variable can be used safely as a lock.
# Take the lock: set it to 1 only if it is currently 0
if btvar -s 1 -eq 0 update_lock
then
do-the-work
btvar -s 0 update_lock
else
echo "someone else holds the lock"
fi
Exit status 0 means the test passed and the assignment was made; 1 means the test failed and nothing was written.
Three limits are worth knowing:
- It works only for a variable this machine owns. A change against another host's variable is forwarded, and the check that makes the operation atomic is applied only to requests that started on the owning machine. Two hosts writing to one exported variable can still lose an update, silently.
- btvar retries a few times and then gives up. Under contention it re-reads, re-tests and re-sends up to five times; after that it stops with a synchronisation error and exit status 4. Treat that as "could not take the lock", not as success.
- Only the assignment is protected. Combining a comparison with -D deletes the variable without the same check, so a delete conditional on a test is not atomic.
Releasing the lock is an ordinary btvar -s and needs no test. What this route does not solve is a script that dies holding the lock; whatever takes the lock must be able to release it on every exit path.
Common Variable Patterns
The patterns below use job conditions (-c) and job assignments (-s with -f). Both are described in full in Controlling Xi-Batch Job Flow with Conditions and Assignments; the forms are shown here so the variable's part in each is visible.
Counter
# Create the counter
btvar -C -s 0 job_count
# Each run of the job adds one at start
btr -f S -s 'job_count+=1' script.sh
Let the scheduler do the arithmetic. A script that reads the value with btvar and writes it back with a second btvar has an unguarded gap between the two, and two jobs doing it at once lose a count.
Status Flag
btvar -C -s "Not Started" backup_status
btr -f S -s 'backup_status=Running' \
-f N -s 'backup_status=Complete' \
-f EA -s 'backup_status=Failed' \
backup.sh
# A later job waits for it
btr -c 'backup_status=Complete' post-backup.sh
Per-host State With One Name
A cluster variable gives every host its own value under a single name, so a job's condition and assignment resolve to the copy belonging to the machine the job ran on.
btvar -C -s Idle -E -K LOCAL_STATUS
On server1 LOCAL_STATUS may read Processing A while on server2 it reads Processing B, and a job running on either sees its own host's value.
A Lock
There are two ways to use a variable as a lock and they are not equally sound.
From a script, with btvar's atomic test-and-set - see Testing and Setting in One Step above. This is guarded by the scheduler and is the route to use where correctness matters.
From a job's condition and assignments - "wait for lock=0, set it to 1 at start, set it back at the end". This is the form older documentation gives, and it is weaker than it looks: the release depends on an end-of-job assignment that does not run if the scheduler is stopped, nothing times the lock out, and nothing reports a job left waiting on one. The Mutual Exclusion section of the conditions and assignments article sets out exactly what holds and what does not.
Best Practices
Use descriptive names, and keep them short. The limit is 19 characters and a longer name is truncated without warning, so two names that differ only after the nineteenth character become the same variable.
Add a comment. Up to 41 characters, and it is what btvlist prints beside the value:
btvar -s "Not Started" -c "Nightly backup state" backup_status
Choose the scope deliberately. Local only for machine-specific state; exported when another host's jobs must see one shared value; cluster when every host needs its own value under a common name.
Set permissions when you create the variable, not afterwards. A variable other jobs only need to read should not be writable by them:
btvar -C -s "Not Started" -M 'U:=RWMPD,GO:=R' backup_status
Give a variable a meaningful starting value. An empty text value and a zero are both indistinguishable from "never set", and a condition on an absent variable holds the job rather than failing it.
Watch the type. The type follows the value, so a variable that has held whole numbers becomes a text variable the moment something stores text in it - and a job assignment that does arithmetic on a text variable is skipped in silence.
Keep an eye on what you export. Every change to an exported variable is written to each connected host as it is made, and the whole set is transferred again whenever a connection is established.