The library must be
sourced
and never executed: it tests
${BASH_SOURCE[0]}
against
${0}
and, when they are equal, prints
utils.sh must be sourced, not executed
on standard error and exits 1.
Source-time actions
Sourcing the library is not side-effect free. In order, it:
1.
Composes
OC_SMF
from
OC_SMF_INSTANCE.
A caller that wants configuration from a non-default SMF instance (for
example
autosync -i NAME
for parallel runs against different backup destinations) must set
OC_SMF_INSTANCEbefore
sourcing; the instance name is baked into
OC_SMF,
PROPPREFIX,
logtag
and any per-instance lock path at source time.
2.
Reads the ZFS user-property prefix with
svcprop -p config/site ${OC_SMF}.
If the property is missing or empty the library prints
PROPPREFIX not initialized
on standard error and exits
78.
This is the only hard failure at source time besides the execute guard.
3.
Derives
script
and
logtag
from
$0.
4.
Reads
config/lock_group
and creates or normalizes
${LOCK_BASE}
(mode 1775, group
${LOCK_GROUP})
through
_ensure_shared_dir(),
because
/var/run
is tmpfs on illumos and is wiped at every boot. See
oc_lock(3).
5.
Reads
config/nodes
and
config/mail_domain,
enumerates the locally visible zones with
zoneadm list -ni,
and records the hostname.
6.
Creates the mail-throttle marker directories: the shared base
${MAIL_THROTTLE_BASE}
at mode 1777 and the per-user directory
${MAIL_THROTTLE_DIR}
at mode 0700; reads
config/mail_throttle_window.
See
oc_log(3).
7.
Defines the SSH option strings and the associative array
_SSH_PROBE_CACHE
used to memoize probe results.
8.
Sets
PFEXEC
from
${EUID}.
Every directory creation and every
chgrp(1)/chmod(1)
normalization is best-effort and silent: failures are discarded so that
sourcing the library never aborts a caller running under
set -e.
Both directory helpers refuse to operate on a path that is a symbolic link,
so that a link planted by an unprivileged user on tmpfs cannot be followed by
a later root invocation.
Reading configuration
Every SMF property this suite reads goes through
oc_svcprop(),
and every property it reads is declared in the service manifest, even when its
default is "unset". The two halves are one convention.
Declaring a property gives it a
type,
so an operator can write
without repeating
astring:,
and
svccfg listprop config/*
shows the full set of knobs rather than only the ones somebody has already
changed. A property that is meant to default to "unset" is declared with
no
value -
<property name='x' type='astring'/>-
never with
<propval value=''/>,
because
svcprop(1)
prints an empty
astring
value as a literal pair of double quotes. A
<propval>
with an empty value therefore hands every reader the two-character string
instead of nothing, which
[ -n ... ]
reads as set. In
zone-mgt
that would invert the meaning of the
config/nfs_opts
test.
oc_svcprop()
closes the same gap from the reading side, for the property an operator clears
by hand with
setprop config/x = \\:
it turns that
back into an empty string, so a cleared property behaves exactly like one that
was never set. Multi-value properties pass through untouched, so
arr=($(oc_svcpropconfig/nodes))
keeps working. It is defined above the source-time reads in
utils.sh
because those run while the file is being sourced.
When adding a property: declare it in
/lib/svc/manifest/omnicube/sysadm.xml,
document it in the SMF PROPERTIES section of
omnicube(7),
and read it with
oc_svcprop().
Global variables
The following variables are set at source time and are part of the interface;
consumers read them directly.
OC_SMF
svc:/omnicube/sysadm:${OC_SMF_INSTANCE:-default}.
OC_SMF_INSTANCE
Not set by the library. Read (if the caller exported it) to select the SMF
instance; unset means
:default.
PROPPREFIX
From SMF property
config/site.
Used as the prefix of the ZFS user properties the suite reads and writes
(for example
${PROPPREFIX}:is_locked,
${PROPPREFIX}:snappolicy).
Mandatory.
script
basename $0.
logtag
${PROPPREFIX}${script/.*/},
i.e. the site prefix followed by the script name with everything from the
first dot removed. This is the tag passed to
logger(1)
and embedded in the subject of every alert mail.
LOCK_BASE
/var/run/omnicube,
hard-coded. Parent of all lock directories and of the mail-throttle tree.
LOCK_GROUP
From SMF property
config/lock_group,
accepted only when it matches
^[a-zA-Z0-9_-]+$;
otherwise
sysadmin.
Group owner of
${LOCK_BASE}.
isolate_lock
${LOCK_BASE}/isolate_node.lock,
the node isolation marker maintained by
isolate_node.sh(8)
and tested by the monitors.
nodes
Array, from SMF property
config/nodes
(an astring list of cluster node names).
host_zones
Array, from
zoneadm list -ni
at source time: the zones installed on this host.
curr_host
hostname.
MAIL_DOMAIN
From SMF property
config/mail_domain.
Used to build the envelope sender
${HOSTNAME}@${MAIL_DOMAIN}.
MAIL_THROTTLE_BASE
${LOCK_BASE}/mail_throttle.
MAIL_THROTTLE_DIR
${MAIL_THROTTLE_BASE}/<user>,
where
<user>
is
id -run
or the literal
unknown
when that fails. Throttling is therefore per uid.
MAIL_THROTTLE_WINDOW
From SMF property
config/mail_throttle_window,
in seconds; accepted only when it matches
^[0-9]+$;
otherwise
3600.
-n -o ConnectTimeout=5 -o ConnectionAttempts=1-o BatchMode=yes -o StrictHostKeyChecking=yes,
used only by the reachability probe. See
oc_ssh(3).
PFEXEC
Empty when
${EUID}
is 0, otherwise
pfexec.
It must be used
unquoted
so that the empty value disappears from the command line instead of being
passed as an empty first argument:
${PFEXEC} zlogin "${zone}" 'svcs -xv'.
The effective uid, not the real uid, is the correct test because the
exec_attr(5)
entries grant these tools uid=0 or euid=0.
_SSH_PROBE_CACHE
is an associative array holding per-process probe verdicts and is private to
oc_ssh(3).
Function index
info(),warning(),error()
Log to stdout/stderr and syslog; warning and error also send throttled mail.
See
oc_log(3).
_send_mail(),_should_send_mail()
Mail delivery and the throttle decision. See
oc_log(3).
acquire_lock_or_exit(),_ensure_shared_dir()
Single-instance locking and shared-directory normalization. See
oc_lock(3).
Argument validation and list/snapshot predicates. See
oc_validate(3).
ssh_reachable(),require_ssh(),ssh_probe_reset()
SSH pre-flight probe and its cache. See
oc_ssh(3).
oc_svcprop()
Read one SMF property, normalizing SMF's spelling of "empty". See
Reading configuration
above.
kill_zone_console(),_zone_console_pids()
Stop a zone's VNC console relay and wait for it, so its pool can be exported.
See
oc_console(3).
sync_zpool()
zpool sync
bounded by
OC_ZPOOL_SYNC_TIMEOUT
seconds; returns non-zero on failure or timeout, and the caller must then not
export the pool. Run before every
zpool export.
The same section of the library defines
OC_ZPOOL_HA_DIR
(/etc/zfs/ha,
live per-pool cachefiles) and
OC_ZPOOL_HA_SAVED_DIR
(/etc/zfs/ha/saved,
the copies a later import reads, since an export deletes the live file).
get_snap_policy_path(),_bkp_priv()
Backup policy resolution and the remote privilege prefix. See
oc_policy(3).
Names beginning with an underscore are internal helpers; they are documented
because their behaviour is visible in logs and in policy files, not because
they are meant to be called from new code.
Portability
The library is
bash
code and is sourced by
#!/usr/bin/bash
scripts. It depends on bash features that ksh93 either lacks or interprets
differently:
${BASH_SOURCE[0]}
for the execute guard,
${HOSTNAME}
in the mail sender,
declare -A
for the probe cache,
[[ ... =~ ... ]]
for every validation pattern,
${script/.*/}
pattern substitution for
logtag,
and
name=(...)
array assignment for
nodes,
host_zones
and
MAIL_DOMAIN.
Under ksh93 the same array syntax creates a compound variable, and
$0
inside a
function name { ... }
block is the function name rather than the script, which would corrupt
script and logtag.
Members of the suite that are not bash, such as
qemu-agent-cmd(8)
(ksh) and
logadm(8)
(sh), therefore do not source this library.
Two functions carry a calling-convention restriction that follows from being
shell state or from calling
exit:
ssh_reachable()
must be called as a statement and not inside
$(),
where cache updates would be discarded with the subshell, and
abort_if_shutting_down()
must not be called inside
$()
or a pipeline, where
exit
would only leave the subshell.
EXIT STATUS
Sourcing the library succeeds silently, or terminates the caller with:
1
The file was executed instead of sourced.
78
PROPPREFIX
is not initialized, i.e.
config/site
is missing on
${OC_SMF}.
The value follows the
EX_CONFIG
convention of
<sysexits.h>.
FILES
/opt/omnicube/lib/common/utils.sh
The library itself.
/var/run/omnicube
${LOCK_BASE};
lock directories, mode 1775, group
${LOCK_GROUP}.
/var/run/omnicube/isolate_node.lock
Node isolation marker.
/var/run/omnicube/mail_throttle
Mail throttle base, mode 1777.
/var/run/omnicube/mail_throttle/<user>
Per-user marker directory, mode 0700.
/var/opt/omnicube/backup/policies/<policy>.conf
Site snapshot policies.
/opt/omnicube/backup/policies/default.conf
Packaged fallback policy.
/etc/nologin
Presence means a shutdown is in progress.
/etc/zfs/ha/<pool>.cache
Live dedicated cachefile of a SYNC zone pool.
/etc/zfs/ha/saved/<pool>.cache
Saved copy read by the next import of that pool.
ENVIRONMENT
OC_SMF_INSTANCE
Selects the SMF instance whose properties are read. Must be set before the
library is sourced. Unset means
default.
OC_IGNORE_RUNLEVEL
When non-empty,
abort_if_shutting_down()
returns instead of exiting. See
oc_runlevel(3).
OC_ZPOOL_SYNC_TIMEOUT
Seconds
sync_zpool()
waits for
zpool sync
before giving up; 120 when unset.
USER
Used by
_send_mail()
as the default recipient; set to
system
when empty.
HOSTNAME
Used in the envelope sender address.
PATH
Must reach
svcprop(1),
zoneadm(8),
zfs(8),
logger(1),
mailx(1),
digest(1),
who(1),
ssh(1)
and
pfexec(1).
EXAMPLES
Example 1: standard prologue of a cron or SMF job
#!/usr/bin/bash
amp;. /opt/omnicube/lib/common/utils.sh
LOCK_DIR=${LOCK_BASE}/${logtag}.myjob.lock
acquire_lock_or_exit "${LOCK_DIR}"
trap '[[ -d ${LOCK_DIR} ]] && rmdir ${LOCK_DIR} 2>/dev/null' EXIT HUP INT TERM
abort_if_shutting_down
info "starting"
Example 2: reading configuration from a non-default instance
OC_SMF_INSTANCE=offsite
amp;. /opt/omnicube/lib/common/utils.sh
info "using ${OC_SMF} with prefix ${PROPPREFIX}"
Example 3: a privileged local command and a remote one
host_zones
and
nodes
are snapshots taken at source time; a zone installed or a node added while a
long-running monitor is in its loop is not seen until the next invocation.
The library reads SMF properties with
svcprop(1)
at source time only. Changing
config/site,
config/nodes,
config/lock_group
or
config/mail_throttle_window
has no effect on processes that are already running.
Sourcing is deliberately tolerant: only a missing
config/site
aborts. A failure to create
${LOCK_BASE}
is not reported at source time; it surfaces later as a lock acquisition
failure from
acquire_lock_or_exit().
man3/omnicube_utils.3generated 2026-10-08 13:21 CEST