Linux & Bash · Part II · Module 25 · EDA Environment and Tool Access
Licence Environment and Diagnosing Licence Failures
A licence failure is the most common reason a correctly installed tool refuses to run, and the most commonly misdiagnosed — because 'no licence available' and 'no licence for this feature' and 'cannot reach the server' are three different problems that people treat as one. This chapter separates them by what the log says.
Intermediate21 min readLinuxPart IILicencesEDADiagnosis
Part II · Module 25 · Chapter 25.4 · Licence Environment and Diagnosing Licence Failures
No real licence manager is installed here — lmstat and lmutil are both absent, as Chapter 25.1 §1 verified. So the diagnosis is demonstrated with a stand-in that reads the same file format and fails in the same four ways. Vendor-specific output is described rather than shown.
1. What a Licence Source Actually Is
The variable does not hold a path. It holds a colon-separated search list, and each element is either a file or a port@host:
$ echo '27000@licsrv1:/proj/lic/site.lic:27000@licsrv2' | tr ':' '\n' | nl
1 27000@licsrv1
2 /proj/lic/site.lic
3 27000@licsrv2Three sources, tried in order, and the first one that grants the feature wins. That has two consequences people trip over:
A broken first entry costs time, not correctness. A dead server at position one means every tool launch waits for a timeout before trying position two. The tool eventually works and everything is slow, which is diagnosed as "the network" far more often than as "the licence list".
And adding a source does not remove one. Appending your own file to the front does not stop the site servers being consulted for features yours does not grant — which is usually what you want, and occasionally the reason a tool picks up an unexpected feature version.
Parsing it is the first thing a diagnostic should do:
$ src=27000@licsrv1:/proj/lic/site.lic:27000@licsrv2
$ IFS=: read -r -a parts <<< "$src"
$ for p in "${parts[@]}"; do
> case $p in
> (*@*) printf ' server %s (port %s host %s)\n' "$p" "${p%%@*}" "${p#*@}" ;;
> (*) printf ' file %s\n' "$p" ;;
> esac
> done
server 27000@licsrv1 (port 27000 host licsrv1)
file /proj/lic/site.lic
server 27000@licsrv2 (port 27000 host licsrv2)IFS=: read -r -a splits on colons into an array, with IFS set for one command so it cannot leak — Chapter 9.2 §6. ${p%%@*} and ${p#*@} take the port and the host with no cut processes — Chapter 8.3. And the case patterns are parenthesised, for the Bash 3.2 reason Chapter 25.1's callout measured.
2. A Licence File, Read as Data
A licence file is text, and the two lines that matter are legible:
$ cat licenses/site.lic
SERVER licsrv1 ANY 27000
FEATURE simtool 2024.0901 31-dec-2026 8 SIGN=A1B2
FEATURE simtool_vip 2024.0901 31-dec-2026 2 SIGN=C3D4
FEATURE othertool 3.2 01-jan-2026 4 SIGN=E5F6SERVER names where the daemon runs. Each FEATURE line is what is licensed, which version, until when, and how many seats. So three questions are answerable with awk and no vendor tool at all:
$ awk '/^FEATURE/ {print $2}' licenses/site.lic
simtool
simtool_vip
othertool
$ awk '$1=="FEATURE" && $2=="simtool" {print "seats: "$5" expires: "$4}' licenses/site.lic
seats: 8 expires: 31-dec-2026Eight seats and an expiry in 2026. That is the difference between "we have no licence for this" and "all eight are in use right now", which are the two most commonly confused licence problems and have completely different remedies.
Chapter 13.7's field model is all this needs. Note $1=="FEATURE" rather than /^FEATURE/ in the second one: a field comparison cannot match the word appearing inside a comment, which a regex can.
3. Four Failures, Four Statuses
The stand-in lmstat reads the file and fails the four ways a real one does.
It works:
$ SIMTOOL_LICENSE_FILE=$PWD/licenses/site.lic ./tools/bin/lmstat
License server status: 27000@licsrv1
FEATURE TOTAL USED EXPIRES
simtool 8 2 31-dec-2026
simtool_vip 2 0 31-dec-2026
othertool 4 1 01-jan-2026
$ echo $?
0No source set:
$ ./tools/bin/lmstat
lmstat: no licence source set (SIMTOOL_LICENSE_FILE)
$ echo $?
2The source names a server it cannot reach:
$ SIMTOOL_LICENSE_FILE=27000@licsrv1 ./tools/bin/lmstat
lmstat: cannot contact license server 27000@licsrv1
$ echo $?
3The source names a file it cannot read:
$ SIMTOOL_LICENSE_FILE=/nope.lic ./tools/bin/lmstat
lmstat: cannot read /nope.lic
$ echo $?
2And from Chapter 25.1 §4, the tool distinguishes three of its own: 3 for an unset variable, 4 for an unreadable file, 5 for a file that grants no feature for it.
Together that is the whole decision tree, and it is worth having in your head because the messages in real tools are longer and less clear:
| Symptom | Means | Fix |
|---|---|---|
| "variable not set" | the environment was never set up | source the setup script |
| "cannot read file" | the path is wrong, or not visible from this machine | check the path, and df it |
| "cannot contact port@host" | network, firewall, or the daemon is down | ask; you cannot fix it |
| "no feature for this tool" | the licence is real and does not cover this tool | ask for the feature |
| "all licenses in use" | the licence covers it and the seats are taken | wait, or queue |
| the tool starts and the design fails | not a licence problem | read the log |
The last row is the one this chapter exists for.
4. Telling a Licence Failure From a Tool or Design Failure
Three failures that look alike, and the one observation that separates each
Three failures that look alike from a distance, separated by one observation each:
A licence failure happens before the tool reads your design. So the log has no compile output, no elaboration, no time-zero messages — it stops at the banner. If your log contains anything about your design, the licence was granted.
A tool failure produces a tool-shaped error and usually a non-zero status, as Chapter 22.2 §3 measured for a syntax error.
A design failure produces a full log and — measured in that same chapter — frequently exit status 0. A run that produced forty thousand lines and exited 0 did not have a licence problem, whatever its last line says.
5. The Diagnostic Script
#!/usr/bin/env bash
# licheck — is the licence environment usable, and what does it grant?
#
# usage: licheck [-v VARNAME] [feature]
# -v the variable holding the licence source (default: LM_LICENSE_FILE)
# exit: 0 usable · 1 a problem was found · 2 usage
set -o nounset
var=LM_LICENSE_FILE
while getopts ':v:' o; do
case $o in
(v) var=$OPTARG ;;
(:) printf 'licheck: -v needs a variable name\n' >&2; exit 2 ;;
(\?) printf 'licheck: unknown option -%s\n' "$OPTARG" >&2; exit 2 ;;
esac
done
shift $(( OPTIND - 1 ))
feature=${1:-}
bad=0
printf -- '-- licence variables that are set\n'
set_any=0
while IFS= read -r line; do
printf ' %s\n' "$line"; set_any=1
done < <(env | grep -iE 'licen[sc]e|_LIC_FILE|^LM_' | sort)
[ "$set_any" -eq 1 ] || { printf ' (none)\n'; bad=1; }
eval "src=\${$var:-}"
printf -- '\n-- %s\n' "$var"
if [ -z "${src:-}" ]; then
printf ' NOT SET\n'; bad=1
else
IFS=: read -r -a parts <<< "$src"
for p in "${parts[@]}"; do
case $p in
(*@*)
printf ' server %-24s port %-6s host %s\n' "$p" "${p%%@*}" "${p#*@}"
printf ' (reachability not tested from here)\n' ;;
('')
printf ' ** an empty element — a stray colon in %s\n' "$var"; bad=1 ;;
(*)
if [ -r "$p" ]; then
printf ' file %s\n' "$p"
n=$(awk '$1=="FEATURE"' "$p" | grep -c . || true)
printf ' %s feature line(s)\n' "$n"
if [ -n "$feature" ]; then
awk -v f="$feature" '$1=="FEATURE" && $2==f {
printf " GRANTS %s version %s expires %s seats %s\n", $2,$3,$4,$5; found=1 }
END { if (!found) printf " does NOT grant %s\n", f }' "$p"
awk -v f="$feature" '$1=="FEATURE" && $2==f {found=1} END {exit !found}' "$p" || bad=1
fi
else
printf ' ** cannot read %s\n' "$p"; bad=1
fi ;;
esac
done
fi
[ "$bad" -eq 0 ]$ SIMTOOL_LICENSE_FILE=$PWD/licenses/site.lic licheck -v SIMTOOL_LICENSE_FILE simtool
-- licence variables that are set
SIMTOOL_LICENSE_FILE=/home/engineer/eda/licenses/site.lic
-- SIMTOOL_LICENSE_FILE
file /home/engineer/eda/licenses/site.lic
3 feature line(s)
GRANTS simtool version 2024.0901 expires 31-dec-2026 seats 8
$ echo $?
0Six decisions:
An empty element is reported. a::b in a licence source is a stray colon, and it is a real and confusing bug — some tools treat an empty element as the current directory. A diagnostic that silently skipped it would hide the cause.
Server reachability is declared untested, not guessed. Testing it needs a TCP connection to a vendor daemon, and claiming otherwise would be the false green Chapter 25.1's callout was about.
The feature check runs awk twice — once to print, once for the status. That is deliberate: an awk that both prints and sets an exit code is harder to read than two passes over a file that is a few hundred lines long, and Chapter 13.7 §6's one-pass argument is about large inputs, not small ones.
$1=="FEATURE" rather than /^FEATURE/, so the word inside a comment cannot match.
eval "src=\${$var:-}" to read a variable named by a variable, with the same caveat as Chapter 25.1 §6: Bash 3.2 has ${!var} but not a clean ${!var:-}, and the name comes from the user.
And every case pattern is parenthesised, because this case sits inside a for inside a script that also uses < <( ) — and once one case in a file needs the form, using it everywhere is less surprising than using it in one place.
Practice
- Run
env | grep -iE 'licen[sc]e|_LIC_|LM_'on a machine with tools installed. How many variables, and do any disagree? - Split your licence source on colons and classify each element as a file or a server.
- For each file element, check it is readable from a compute node, not just from your workstation.
- Count the
FEATURElines in a licence file you can read. Which features does it grant? - Find the seat count and expiry for a feature you use.
- Put a deliberately dead
port@hostat the front of your licence source and time a tool launch. How much slower? - Add a stray double colon and see what your tool does.
- Find a log from a licence failure. Does it contain anything about your design?
- Find a log where the tool ran and the test failed. What was the exit status?
- Write the
tail -20 | grep -i licencecheck and run it over a directory of old logs. Any hits? - Write
licheckand run it with a correct environment, then with the variable unset, then pointing at an unreadable file. - Ask it about a feature the licence does not grant and confirm exit 1.
Check Your Understanding
- What does a licence variable actually contain?
- What are the two consequences of it being an ordered list?
- Why is a vendor-specific variable better practice than
LM_LICENSE_FILE? - What four things does a
FEATUREline tell you? - Why
$1=="FEATURE"rather than/^FEATURE/? - What are the two licence problems people most often confuse?
- How do you tell a licence failure from a design failure by looking at the log?
- What is the one case where a licence message appears at the end of a full log?
- Why is that case usually dismissed as flaky?
- Why does
lichecksay server reachability is untested? - Why is an empty element in a licence source worth reporting?
6. Remember
- A licence variable is a colon-separated search list, each element a file or a
port@host. - It is ordered: a dead first entry makes launches slow, not failed.
- Prefer the vendor-specific variable to
LM_LICENSE_FILE, which makes every tool ask every server. - A
FEATUREline gives the feature, version, expiry and seat count — all readable withawk. - "No licence for this tool" and "all seats in use" are different problems. The seat count tells you which.
- Use
$1=="FEATURE", not a line-anchored regex. - Measured: four failures, four statuses — unset (2), unreadable file (2), unreachable server (3) from the status tool; and 3, 4, 5 from the tool itself for unset, unreadable and no-feature.
- A licence failure stops the log at the banner. Anything about your design means the licence was granted.
- A design failure often exits 0 with a full log — not a licence problem.
- A mid-run licence loss is the exception: a healthy log that stops, with the licence message at the end. Check
tail -20 | grep -i licen. - Report an empty element in the source — a stray colon is a real bug.
- Do not claim to have tested a server you did not contact.
awk'sexitruns theENDblock, andEND'sexitreplaces your status. Set a flag andexit !foundinEND, or letgrep -qxown the status.- Run a diagnostic against a known-good environment too. A false red gets switched off as fast as a false green.
- Parenthesise
casepatterns in anything that also uses$( )or< <( )under Bash 3.2.
7. What Is Next
Chapter 25.5 moves from the tool's own variables to the project's: the small agreed set that stops scripts containing absolute paths which resolve on exactly one person's machine.
Where this fits
Part of the Linux & Bash curriculum.
