4 138e225e 2025-06-04 xs # Copyright 2025 xs <xs@inda.re>
6 138e225e 2025-06-04 xs # Permission to use, copy, modify, and/or distribute this software for any
7 138e225e 2025-06-04 xs # purpose with or without fee is hereby granted, provided that the above
8 138e225e 2025-06-04 xs # copyright notice and this permission notice appear in all copies.
10 138e225e 2025-06-04 xs # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
11 138e225e 2025-06-04 xs # REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
12 138e225e 2025-06-04 xs # FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
13 138e225e 2025-06-04 xs # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14 138e225e 2025-06-04 xs # LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
15 138e225e 2025-06-04 xs # OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 138e225e 2025-06-04 xs # PERFORMANCE OF THIS SOFTWARE.
18 46924c01 2025-05-28 xs # shellcheck disable=SC2046,SC2031
20 03d8f521 2025-05-27 xs # Global configuration
21 03d8f521 2025-05-27 xs # ====================
23 8e44e3d4 2025-06-25 xs : "${ZSH_VERSION:=}"
26 8e44e3d4 2025-06-25 xs _init_pwd="$PWD"
28 46924c01 2025-05-28 xs # shellcheck disable=SC2031
30 46924c01 2025-05-28 xs _debug _INIT_ENV
31 8e44e3d4 2025-06-25 xs cd "$_init_pwd" || _fail "Unable to cd to '$_init_pwd'"
33 46924c01 2025-05-28 xs # Here is the default configuration
34 46924c01 2025-05-28 xs : "${R7_DEBUG:=no}"
35 46924c01 2025-05-28 xs : "${R7_DRYRUN:=no}"
36 31e019a2 2025-05-31 xs : "${R7_GROUP_ALLPREFIX:=no}"
37 31e019a2 2025-05-31 xs : "${R7_GROUP_SUMMARY:=no}"
38 730e151b 2025-06-02 xs : "${R7_GROUP_EXITONERROR:=no}"
39 46924c01 2025-05-28 xs : "${R7_PARALLEL:=no}"
40 46924c01 2025-05-28 xs : "${R7_PREFIX:=install state doc}"
41 31e019a2 2025-05-31 xs : "${R7_SHOWOUTPUT:=no}"
42 8e44e3d4 2025-06-25 xs : "${R7_WORKDIR:=$_init_pwd}"
43 46924c01 2025-05-28 xs : "${R7_SITE_CONFIG:=$R7_WORKDIR/site-config}"
44 31e019a2 2025-05-31 xs : "${R7_OUTPUTDIR:=$R7_WORKDIR/_output}"
45 31e019a2 2025-05-31 xs : "${SSH_CONFIG_DIR:=$R7_WORKDIR/.ssh}"
46 46924c01 2025-05-28 xs : "${SSH_CONFIG_FILE:=$R7_WORKDIR/.ssh/config}"
47 31e019a2 2025-05-31 xs : "${SSH_CONTROL_DIR:=$SSH_CONFIG_DIR/control}"
48 46924c01 2025-05-28 xs : "${SSH_IDENTITY_FILE:=$R7_WORKDIR/.ssh/id_ed25519_r7}"
49 f83445b1 2025-05-30 xs : "${SSH_KNOWN_HOSTS:=$R7_WORKDIR/.ssh/known_hosts}"
50 31e019a2 2025-05-31 xs : "${SSH_CONNECT_TIMEOUT:=3}"
52 46924c01 2025-05-28 xs _debug ENVIRONMENT
54 46924c01 2025-05-28 xs # Validate environment
55 46924c01 2025-05-28 xs if test "$R7_DEBUG" = yes; then
56 46924c01 2025-05-28 xs set | grep -e '^R7_' -e '^SSH' 1>&2
59 46924c01 2025-05-28 xs for param in "$R7_DEBUG" "$R7_DRYRUN" "$R7_PARALLEL"; do
60 46924c01 2025-05-28 xs case $param in
61 46924c01 2025-05-28 xs yes | no) : ;;
62 46924c01 2025-05-28 xs *) _fail "Invalid parameter value: '$param'" ;;
66 46924c01 2025-05-28 xs R7_WORKDIR_tmp=$(_resolv_dir "$R7_WORKDIR")
67 730e151b 2025-06-02 xs test -d "$R7_WORKDIR_tmp" || _error "Working directory not found: '$R7_WORKDIR'"
68 46924c01 2025-05-28 xs R7_WORKDIR=$R7_WORKDIR_tmp
69 46924c01 2025-05-28 xs R7_RUNDIR=$R7_WORKDIR/_run
70 46924c01 2025-05-28 xs R7_OUTPUTDIR=$R7_WORKDIR/_output
72 46924c01 2025-05-28 xs R7_SITE_CONFIG_tmp=$(_resolv_file "$R7_SITE_CONFIG")
73 730e151b 2025-06-02 xs test -f "$R7_SITE_CONFIG_tmp" || _error "Site configuration not found: '$R7_SITE_CONFIG'"
74 46924c01 2025-05-28 xs R7_SITE_CONFIG=$R7_SITE_CONFIG_tmp
76 46924c01 2025-05-28 xs SSH_CONFIG_FILE_tmp=$(_resolv_file "$SSH_CONFIG_FILE")
77 730e151b 2025-06-02 xs test -f "$SSH_CONFIG_FILE_tmp" || _error "Unable to read SSH configuration: '$SSH_CONFIG_FILE'"
78 46924c01 2025-05-28 xs SSH_CONFIG_FILE=$SSH_CONFIG_FILE_tmp
80 46924c01 2025-05-28 xs SSH_IDENTITY_FILE_tmp=$(_resolv_file "$SSH_IDENTITY_FILE")
81 730e151b 2025-06-02 xs test -f "$SSH_IDENTITY_FILE_tmp" || _error "Unable to read SSH identity file: '$SSH_IDENTITY_FILE'"
82 46924c01 2025-05-28 xs SSH_IDENTITY_FILE=$SSH_IDENTITY_FILE_tmp
84 0253bef1 2025-05-29 xs R7_ENV_READY=yes
85 46924c01 2025-05-28 xs unset R7_WORKDIR_tmp R7_SITE_CONFIG_tmp SSH_CONFIG_FILE_tmp SSH_IDENTITY_FILE_tmp
92 730e151b 2025-06-02 xs printf "FAILURE: %s\n" "$*" >&2
97 730e151b 2025-06-02 xs printf "ERROR: %s\n" "$*" >&2
101 f83445b1 2025-05-30 xs test "$R7_DEBUG" = yes && printf %s\\n "${*:-$(cat -)}" >&2
105 0253bef1 2025-05-29 xs if test "$R7_ENV_READY" = yes; then
106 0253bef1 2025-05-29 xs cd "$R7_WORKDIR" || return 2
107 f6bdfddc 2025-06-03 xs _trace_id_last=$(trace_id_last)
108 f6bdfddc 2025-06-03 xs if test -n "$_trace_id_last"; then
109 f6bdfddc 2025-06-03 xs printf -- "- (%s) - in %s the %s\n" \
110 f6bdfddc 2025-06-03 xs "$_trace_id_last" "$(basename "$R7_WORKDIR")" "$(_epoch "$_trace_id_last")"
111 f6bdfddc 2025-06-03 xs nodes_active | sed 's/^/ - (active) /'
120 46924c01 2025-05-28 xs alias help=usage
123 2f0c97a2 2025-06-04 xs version: $VERSION
124 730e151b 2025-06-02 xs usage: $0 [options] [command [arguments...]]
125 03d8f521 2025-05-27 xs usage options:
126 03d8f521 2025-05-27 xs -h Show usage and exit
128 03d8f521 2025-05-27 xs -i SSH identity file
129 03d8f521 2025-05-27 xs -F SSH client configuration file
130 03d8f521 2025-05-27 xs -w Work directory where to find ./nodes and ./groups
131 03d8f521 2025-05-27 xs -r Run directory, default to ./_run
132 03d8f521 2025-05-27 xs -o Output directory, default to ./_output
133 03d8f521 2025-05-27 xs -c Site configuration file, default to ./site-config
134 03d8f521 2025-05-27 xs -n Dry run, prevent any host modification
135 730e151b 2025-06-02 xs -e Exit the group execution on the error function
136 03d8f521 2025-05-27 xs -p Run each host in parallel per group call
137 0253bef1 2025-05-29 xs -O Show output interactively
138 4296d94d 2025-06-07 xs -P Prefix, can contain whole function names
139 0253bef1 2025-05-29 xs -a Select all available prefixes for a group command
140 0253bef1 2025-05-29 xs -s Output-dump and summary are applied after a group command
143 03d8f521 2025-05-27 xs run-config Run the site configuration
144 03d8f521 2025-05-27 xs group Deploy specified group to hosts
145 03d8f521 2025-05-27 xs output-dump Dump the run directory to the output directory
146 03d8f521 2025-05-27 xs output Consult the output
147 0253bef1 2025-05-29 xs summary Summary of errors and changes
148 03d8f521 2025-05-27 xs digraph-host Produce digraph host
149 730e151b 2025-06-02 xs digraph-ssh Produce SSH IDs digraph
150 730e151b 2025-06-02 xs import Import TARGET host in current nodes directory
151 87bce7c5 2025-05-30 xs info Default command showing active nodes and summary
152 31e019a2 2025-05-31 xs init Initialize a new work directory
154 03d8f521 2025-05-27 xs site-config format:
155 03d8f521 2025-05-27 xs prefix [prefix...] # Define the prefix to be executed in order
156 03d8f521 2025-05-27 xs group groupname ['.*'|hostname...] # Deploy specified group to hosts
161 03d8f521 2025-05-27 xs # shellcheck disable=SC2086
163 730e151b 2025-06-02 xs test -n "$ZSH_VERSION" && {
164 730e151b 2025-06-02 xs echo >&2 "ZSH is unsupported. Exiting."
167 730e151b 2025-06-02 xs while getopts 'P:F:r:o:i:w:c:npdseaOh' arg; do
175 46924c01 2025-05-28 xs _debug "R7 DEBUG $VERSION"
177 03d8f521 2025-05-27 xs i) SSH_IDENTITY_FILE=$OPTARG ;;
178 03d8f521 2025-05-27 xs F) SSH_CONFIG_FILE=$OPTARG ;;
179 46924c01 2025-05-28 xs w) R7_WORKDIR=$OPTARG ;;
180 46924c01 2025-05-28 xs r) R7_RUNDIR=$OPTARG ;;
181 46924c01 2025-05-28 xs o) R7_OUTPUTDIR=$OPTARG ;;
182 0253bef1 2025-05-29 xs s) R7_GROUP_SUMMARY=yes ;;
183 0253bef1 2025-05-29 xs a) R7_GROUP_ALLPREFIX=yes ;;
184 730e151b 2025-06-02 xs e) R7_GROUP_EXITONERROR=yes ;;
185 46924c01 2025-05-28 xs c) R7_SITE_CONFIG=$OPTARG ;;
187 46924c01 2025-05-28 xs R7_DRYRUN=yes
188 46924c01 2025-05-28 xs _debug "MODE DRYRUN"
190 46924c01 2025-05-28 xs p) R7_PARALLEL=yes ;;
192 46924c01 2025-05-28 xs R7_PREFIX=$OPTARG
193 46924c01 2025-05-28 xs prefix $R7_PREFIX
195 46924c01 2025-05-28 xs O) R7_SHOWOUTPUT=yes ;;
198 0253bef1 2025-05-29 xs echo 1>&2 "Unknown option '$arg'"
203 03d8f521 2025-05-27 xs shift $((OPTIND - 1))
206 46924c01 2025-05-28 xs init | workdir-init)
208 730e151b 2025-06-02 xs _workdir_init "$@"
216 730e151b 2025-06-02 xs rc | run-config) run_config ;;
219 46924c01 2025-05-28 xs trace_id=$(_trace_id_new)
220 0253bef1 2025-05-29 xs if test "$R7_GROUP_ALLPREFIX" = yes; then
221 0253bef1 2025-05-29 xs prefix $(group_prefixes "$1")
223 87bce7c5 2025-05-30 xs prefix $R7_PREFIX
226 0253bef1 2025-05-29 xs if test "$R7_GROUP_SUMMARY" = yes; then
231 87bce7c5 2025-05-30 xs info) info ;;
236 87bce7c5 2025-05-30 xs out | output)
240 87bce7c5 2025-05-30 xs outdump | output-dump) output_dump ;;
241 0253bef1 2025-05-29 xs summary) summary ;;
242 03d8f521 2025-05-27 xs digraph-host)
243 46924c01 2025-05-28 xs prefix $R7_PREFIX
247 730e151b 2025-06-02 xs digraph_ssh_id_hosts
249 0551e9c2 2025-06-04 xs html) html "$@" ;;
250 8e44e3d4 2025-06-25 xs '') cd "$R7_WORKDIR" && info ;;
253 730e151b 2025-06-02 xs -) exit 33 ;;
255 46924c01 2025-05-28 xs echo 1>&2 "Unknown command '$1'"
259 730e151b 2025-06-02 xs *) prefix $R7_PREFIX ;;
265 46924c01 2025-05-28 xs _resolv_dir() {
266 46924c01 2025-05-28 xs if test -d "$1"; then
267 46924c01 2025-05-28 xs cd "$1" || return 2
268 46924c01 2025-05-28 xs printf %s "$PWD"
272 46924c01 2025-05-28 xs _resolv_file() {
273 46924c01 2025-05-28 xs if test -f "$1"; then
274 46924c01 2025-05-28 xs cd "${1%/*}" || return 2
275 46924c01 2025-05-28 xs printf %s "$PWD/${1##*/}"
279 46924c01 2025-05-28 xs # shellcheck disable=SC2030
280 46924c01 2025-05-28 xs _workdir_init() (
281 730e151b 2025-06-02 xs _debug _WORKDIR_INIT "$PWD" "$R7_WORKDIR"
282 730e151b 2025-06-02 xs if test -n "$R7_WORKDIR"; then
283 730e151b 2025-06-02 xs mkdir -p "$R7_WORKDIR" || _fail "Unable to create directory: '$R7_WORKDIR'"
284 730e151b 2025-06-02 xs cd "$R7_WORKDIR" || _fail "Unable to cd to directory: '$R7_WORKDIR'"
285 730e151b 2025-06-02 xs R7_WORKDIR=$PWD
287 730e151b 2025-06-02 xs R7_WORKDIR=$PWD
288 46924c01 2025-05-28 xs mkdir -p groups
289 46924c01 2025-05-28 xs mkdir -p nodes
290 730e151b 2025-06-02 xs mkdir -p .ssh/control
291 730e151b 2025-06-02 xs : "${SSH_CONFIG_FILE:=$R7_WORKDIR/.ssh/config}"
292 730e151b 2025-06-02 xs : "${SSH_IDENTITY_FILE:=$R7_WORKDIR/.ssh/id_ed25519_r7}"
293 46924c01 2025-05-28 xs R7_SITE_CONFIG=./site-config
294 46924c01 2025-05-28 xs if ! test -f "$R7_SITE_CONFIG"; then
295 46924c01 2025-05-28 xs cat >"$R7_SITE_CONFIG" <<-eof
296 46924c01 2025-05-28 xs # Example r7 site configuration
297 730e151b 2025-06-02 xs group common '.*'
300 46924c01 2025-05-28 xs if ! test -f "$SSH_CONFIG_FILE"; then
301 46924c01 2025-05-28 xs cat >"$SSH_CONFIG_FILE" <<eof
302 46924c01 2025-05-28 xs # Example SSH client configuration
304 46924c01 2025-05-28 xs AddKeysToAgent yes
305 46924c01 2025-05-28 xs BatchMode yes
306 46924c01 2025-05-28 xs ConnectTimeout 3
307 46924c01 2025-05-28 xs ConnectionAttempts 1
308 46924c01 2025-05-28 xs ControlMaster auto
309 730e151b 2025-06-02 xs #ControlPath $R7_WORKDIR/.ssh/control/%C
310 730e151b 2025-06-02 xs ControlPath $R7_WORKDIR/.ssh/control/%r@%h:%p
311 46924c01 2025-05-28 xs ControlPersist 2m
312 46924c01 2025-05-28 xs IdentityFile $SSH_IDENTITY_FILE
313 46924c01 2025-05-28 xs StrictHostKeyChecking yes
315 46924c01 2025-05-28 xs UserKnownHostsFile $R7_WORKDIR/.ssh/known_hosts
318 31e019a2 2025-05-31 xs touch "${SSH_IDENTITY_FILE}" "${SSH_IDENTITY_FILE}.pub"
319 46924c01 2025-05-28 xs touch .ssh/known_hosts
320 46924c01 2025-05-28 xs echo Created:
321 46924c01 2025-05-28 xs find "$PWD" -exec printf -- "\t- %s\n" {} +
324 46924c01 2025-05-28 xs # SSH functions
325 03d8f521 2025-05-27 xs # =============
328 46924c01 2025-05-28 xs _debug "SSH $*"
329 03d8f521 2025-05-27 xs /usr/bin/ssh "-oConnectTimeout=$SSH_CONNECT_TIMEOUT" -F "$SSH_CONFIG_FILE" "$@"
332 f83445b1 2025-05-30 xs alias ssh-debug=ssh_debug
333 f83445b1 2025-05-30 xs ssh_debug() {
334 f83445b1 2025-05-30 xs _debug "SSH $*"
335 8e44e3d4 2025-06-25 xs ssh -vvv "$@"
338 f83445b1 2025-05-30 xs alias ssh-known-hosts=ssh_known_hosts
339 f83445b1 2025-05-30 xs ssh_known_hosts() {
340 f83445b1 2025-05-30 xs /usr/bin/ssh-keygen -lv -f "${SSH_KNOWN_HOSTS:-$R7_WORKDIR/.ssh/known_hosts}"
343 f83445b1 2025-05-30 xs alias ssh-authorized-keys-reset=ssh_authorized_keys_reset
344 f83445b1 2025-05-30 xs alias ssh-authreset=ssh_authorized_keys_reset
345 f83445b1 2025-05-30 xs ssh_authorized_keys_reset() {
346 f83445b1 2025-05-30 xs ssh -o BatchMode=no "$@" "
347 f83445b1 2025-05-30 xs mkdir -p ~/.ssh
348 f83445b1 2025-05-30 xs chmod 700 ~/.ssh
349 f83445b1 2025-05-30 xs touch ~/.ssh/authorized_keys
350 f83445b1 2025-05-30 xs chmod 600 ~/.ssh/authorized_keys
351 f83445b1 2025-05-30 xs echo '$(cat "$SSH_IDENTITY_FILE.pub")' >~/.ssh/authorized_keys
355 f83445b1 2025-05-30 xs alias ssh-accept-new=ssh_accept_new
356 f83445b1 2025-05-30 xs ssh_accept_new() {
357 8e44e3d4 2025-06-25 xs ssh -F "$SSH_CONFIG_FILE" -o StrictHostKeyChecking=accept-new "$@" :
360 f83445b1 2025-05-30 xs alias ssh-control-master-clean=ssh_control_master_clean
361 f83445b1 2025-05-30 xs ssh_control_master_clean() {
362 31e019a2 2025-05-31 xs find "$SSH_CONTROL_DIR" -type f -exec rm {} \;
365 f83445b1 2025-05-30 xs alias ssh-users=ssh_users
366 f83445b1 2025-05-30 xs ssh_users() {
367 730e151b 2025-06-02 xs cd "$R7_WORKDIR/nodes" || _fail "Unable to cd to '$R7_WORKDIR/nodes'."
368 730e151b 2025-06-02 xs find ./* -type f -name "authorized_keys_*" |
369 3aa5672d 2025-06-04 xs sed 's;^./;;' |
370 f83445b1 2025-05-30 xs sort | while read -r keyfile; do
371 f83445b1 2025-05-30 xs echo "- $keyfile:"
372 f83445b1 2025-05-30 xs grep -v "^#" "$R7_WORKDIR/nodes/$keyfile"
376 f83445b1 2025-05-30 xs alias ssh-ids=ssh_ids
378 f83445b1 2025-05-30 xs ssh_users | grep -v ^- | sort -u
381 f83445b1 2025-05-30 xs alias ssh-authorized-keys=ssh_authorized_keys
382 f83445b1 2025-05-30 xs alias ssh-auth=ssh_authorized_keys
383 f83445b1 2025-05-30 xs ssh_authorized_keys() {
384 f83445b1 2025-05-30 xs ssh_users | grep -e ^- | sed -e 's/^- /nodes\//' -e 's/:$//'
387 f83445b1 2025-05-30 xs alias ssh-ids-hosts=ssh_ids_hosts
388 f83445b1 2025-05-30 xs # shellcheck disable=SC2046
389 f83445b1 2025-05-30 xs ssh_ids_hosts() {
390 f83445b1 2025-05-30 xs ssh_ids | awk '{ print $3 }' |
391 f83445b1 2025-05-30 xs while read -r id; do
392 f83445b1 2025-05-30 xs grep -e "$id" $(ssh_authorized_keys)
393 f83445b1 2025-05-30 xs done | while IFS="$IFS/:" read -r _ hostname _ keyfile _ _ comment; do
394 f83445b1 2025-05-30 xs printf '"%s" -> "%s" -> "%s"\n' "${comment#* }" "${keyfile#authorized_keys_}" "$hostname"
399 03d8f521 2025-05-27 xs eval "$(ssh-agent)"
400 03d8f521 2025-05-27 xs ssh-add "$SSH_IDENTITY_FILE"
404 87bce7c5 2025-05-30 xs test "$R7_DRYRUN" = yes && return 0
405 46924c01 2025-05-28 xs _debug "COPY $1 $2 BEGIN"
406 03d8f521 2025-05-27 xs node=${2%%:*}
408 03d8f521 2025-05-27 xs if r=$(ssh "$node" command -v openrsync); then
409 03d8f521 2025-05-27 xs if openrsync -V 2>/dev/null; then
410 46924c01 2025-05-28 xs _debug "COPY $1 $2 USE OPENRSYNC"
411 03d8f521 2025-05-27 xs openrsync -e "/usr/bin/ssh -oConnectTimeout=$SSH_CONNECT_TIMEOUT -F $SSH_CONFIG_FILE" \
412 03d8f521 2025-05-27 xs --rsync-path="$r" \
414 03d8f521 2025-05-27 xs -a "$1" "$2" && return 0
417 03d8f521 2025-05-27 xs if r=$(ssh "$node" command -v rsync); then
418 03d8f521 2025-05-27 xs if rsync -V 1>/dev/null; then
419 46924c01 2025-05-28 xs _debug "COPY $1 $2 USE RSYNC"
420 03d8f521 2025-05-27 xs rsync -e "/usr/bin/ssh -oConnectTimeout=$SSH_CONNECT_TIMEOUT -F $SSH_CONFIG_FILE" \
421 03d8f521 2025-05-27 xs --rsync-path="$r" \
423 03d8f521 2025-05-27 xs -aq "$1" "$2" && return 0
426 46924c01 2025-05-28 xs _debug "COPY $1 $2 USE SCP"
427 03d8f521 2025-05-27 xs dir=${dir:-/tmp/r7}
428 03d8f521 2025-05-27 xs ssh "$node" rm -fr "$dir/$1" 2>/dev/null
429 8e44e3d4 2025-06-25 xs scp "-oConnectTimeout=$SSH_CONNECT_TIMEOUT" -qF "$SSH_CONFIG_FILE" -r "$1" "$2" && return 0
430 8e44e3d4 2025-06-25 xs _fail "Unable to copy '$1' to '$2'."
433 03d8f521 2025-05-27 xs # Deployment functions
434 03d8f521 2025-05-27 xs # ====================
436 46924c01 2025-05-28 xs _trace_id_new() { date +%s; }
439 46924c01 2025-05-28 xs cd "$R7_WORKDIR" || return 2
440 0253bef1 2025-05-29 xs test -d ./nodes || return 0
441 03d8f521 2025-05-27 xs find nodes -type d -maxdepth 1 | tail -n +2 | awk -F/ '{ print $2 }'
444 46924c01 2025-05-28 xs alias nodes-active=nodes_active
445 46924c01 2025-05-28 xs alias nodesact=nodes_active
446 03d8f521 2025-05-27 xs nodes_active() (
447 f83445b1 2025-05-30 xs test "$R7_DRYRUN" = yes && {
451 46924c01 2025-05-28 xs if test -s "$R7_WORKDIR/.active_nodes"; then
452 46924c01 2025-05-28 xs cat "$R7_WORKDIR/.active_nodes"
454 46924c01 2025-05-28 xs touch "$R7_WORKDIR/.active_nodes"
455 03d8f521 2025-05-27 xs for node in $(nodes); do
456 46924c01 2025-05-28 xs _debug "ACTIVE $node"
457 03d8f521 2025-05-27 xs if ssh "$node" :; then
458 46924c01 2025-05-28 xs printf "%s\\n" "$node" >>"$R7_WORKDIR/.active_nodes"
461 46924c01 2025-05-28 xs cat "$R7_WORKDIR/.active_nodes"
465 46924c01 2025-05-28 xs alias run-config=run_config
466 46924c01 2025-05-28 xs alias rc=run_config
467 46924c01 2025-05-28 xs # shellcheck disable=SC1090,SC2031
468 03d8f521 2025-05-27 xs run_config() {
469 46924c01 2025-05-28 xs trace_id=$(_trace_id_new)
470 03d8f521 2025-05-27 xs # shellcheck disable=SC1091
471 46924c01 2025-05-28 xs _debug LOADING "$R7_SITE_CONFIG"
472 f6bdfddc 2025-06-03 xs sh -n "$R7_SITE_CONFIG" && . "$R7_SITE_CONFIG"
475 03d8f521 2025-05-27 xs # Inherits $target from group()
476 03d8f521 2025-05-27 xs # shellcheck disable=SC2031
478 46924c01 2025-05-28 xs _debug "DEPLOY $new_name $target"
479 46924c01 2025-05-28 xs cd "$R7_WORKDIR" || exit 2
480 03d8f521 2025-05-27 xs stem="groups/$new_name"
481 03d8f521 2025-05-27 xs nodedir="nodes/$target"
482 0253bef1 2025-05-29 xs test "$R7_DRYRUN" = yes || {
483 0253bef1 2025-05-29 xs if test -d "$stem.dir"; then
484 8e44e3d4 2025-06-25 xs ssh "$target" /bin/sh -c 'umask 027; mkdir -p /tmp/r7'
485 03d8f521 2025-05-27 xs copy "$stem.dir" "$target:/tmp/r7" || return 3
487 0253bef1 2025-05-29 xs if test -d "$nodedir"; then
488 03d8f521 2025-05-27 xs test -f /tmp/r7/"$trace_id.$target" || {
489 8e44e3d4 2025-06-25 xs ssh "$target" /bin/sh -c 'umask 027; mkdir -p /tmp/r7'
490 03d8f521 2025-05-27 xs copy "$nodedir" "$target:/tmp/r7" || return 3
494 03d8f521 2025-05-27 xs if test -f "$stem"; then
495 0253bef1 2025-05-29 xs if _payload "$stem" | sh -n; then
496 0253bef1 2025-05-29 xs _debug SYNTAX OK "$stem"
497 0253bef1 2025-05-29 xs test "$R7_DRYRUN" = yes || {
498 0253bef1 2025-05-29 xs _payload "$stem" | ssh 2>&1 "$target" /bin/sh - |
499 0253bef1 2025-05-29 xs _collect "$R7_RUNDIR/$trace_id.$target.$new_name"
502 0253bef1 2025-05-29 xs _debug SYNTAX KO "$stem"
503 0253bef1 2025-05-29 xs echo 1>&2 "syntax error in '$stem', skipping..."
510 03d8f521 2025-05-27 xs set -f -- $(printf -- "-e %s " "$@")
511 03d8f521 2025-05-27 xs nodes_active | grep "$@"
514 0253bef1 2025-05-29 xs # shellcheck disable=SC2030
516 46924c01 2025-05-28 xs test $# -eq 0 && return 0
517 46924c01 2025-05-28 xs cd "$R7_WORKDIR" || return 2
519 0253bef1 2025-05-29 xs test "$R7_DRYRUN" = yes || {
520 0253bef1 2025-05-29 xs mkdir -p "$R7_RUNDIR" || return 2
521 0253bef1 2025-05-29 xs cd "$R7_RUNDIR" || return 2
523 46924c01 2025-05-28 xs _debug "CREATED $R7_RUNDIR"
525 46924c01 2025-05-28 xs _debug "PROCESSING GROUP $new_name"
527 03d8f521 2025-05-27 xs # shellcheck disable=SC2046
529 03d8f521 2025-05-27 xs for target in $(members "$@"); do
530 46924c01 2025-05-28 xs case $R7_PARALLEL in
542 0253bef1 2025-05-29 xs group_prefixes() (
543 0253bef1 2025-05-29 xs grep 2>/dev/null -e "^.*()" "$R7_WORKDIR/groups/$1" | cut -d\( -f1 | cut -d_ -f1 | uniq
546 46924c01 2025-05-28 xs alias group-functions=group_functions
547 46924c01 2025-05-28 xs alias groupfuncs=group_functions
548 46924c01 2025-05-28 xs group_functions() (
549 0253bef1 2025-05-29 xs if test "$R7_GROUP_ALLPREFIX" = yes; then
550 8fe13599 2025-06-05 xs grep 2>/dev/null -e "^[^#].*()" "$R7_WORKDIR/groups/$1" | cut -d\( -f1
553 03d8f521 2025-05-27 xs for pre in $prefix; do
554 2ae3e68f 2025-06-07 xs grep 2>/dev/null -e "^${pre}.*()" "$R7_WORKDIR/groups/$1" | cut -d\( -f1
558 8fe13599 2025-06-05 xs alias group-functions-all=group_functions_all
559 8fe13599 2025-06-05 xs alias groupfuncsall=group_functions_all
560 8fe13599 2025-06-05 xs group_functions_all() (
561 8fe13599 2025-06-05 xs set -- $(cd "$R7_WORKDIR/groups" && find ./* -type f -maxdepth 0)
563 8fe13599 2025-06-05 xs group_functions "${file##./}" | while read -r func; do
564 8fe13599 2025-06-05 xs printf %s::%s\\n "${file##./}" "$func"
571 730e151b 2025-06-02 xs R7_PREFIX=$prefix
572 730e151b 2025-06-02 xs export prefix R7_PREFIX
576 46924c01 2025-05-28 xs cd "$R7_WORKDIR" || return 2
577 2ae3e68f 2025-06-07 xs grep -e "^$2.*()" "$1" |
578 03d8f521 2025-05-27 xs cut -d\( -f1 | while read -r function; do
579 46924c01 2025-05-28 xs _debug "CALL $1 $function"
580 03d8f521 2025-05-27 xs echo trace "${1#*/}" "$function" START
581 03d8f521 2025-05-27 xs echo "_ret=; $function || { _ret=\$?; : ;}"
582 03d8f521 2025-05-27 xs echo trace "${1#*/}" "$function" END "\${_ret:-0}"
583 f83445b1 2025-05-30 xs test "$R7_DRYRUN" = yes && {
584 f83445b1 2025-05-30 xs echo 1>&2 "- (dryrun) $target:$new_name::$function"
590 8e44e3d4 2025-06-25 xs echo "hostname=$target"
591 03d8f521 2025-05-27 xs echo "trace_id=$trace_id"
592 0253bef1 2025-05-29 xs echo "groupname=$new_name"
593 730e151b 2025-06-02 xs echo "exitonerror=$R7_GROUP_EXITONERROR"
596 03d8f521 2025-05-27 xs echo ":: $trace_id $(nodename) $*" 1>&2
600 46924c01 2025-05-28 xs trace ERROR "$*" 1>&2
601 730e151b 2025-06-02 xs test "$exitonerror" = yes && {
602 730e151b 2025-06-02 xs case $_ret in (0) _ret=1 ;; esac
603 730e151b 2025-06-02 xs trace EXIT $_ret
608 46924c01 2025-05-28 xs hostname=${hostname:-$(uname -n)}
609 03d8f521 2025-05-27 xs echo "${hostname%%.*}"
612 03d8f521 2025-05-27 xs atom=/tmp/r7/$(nodename)/$1
613 b7a0ae1e 2025-05-27 xs test -f "$atom" && return 0
614 b7a0ae1e 2025-05-27 xs test -d "$atom" && return 0
618 03d8f521 2025-05-27 xs echo "/tmp/r7/$(nodename)"
621 0253bef1 2025-05-29 xs echo "/tmp/r7/$groupname.dir"
624 730e151b 2025-06-02 xs if sh -n "$1"; then
625 730e151b 2025-06-02 xs trace SOURCE "$1"
628 730e151b 2025-06-02 xs trace SOURCE FAILED "$1"
633 8e44e3d4 2025-06-25 xs test $# -gt 0 || {
634 8e44e3d4 2025-06-25 xs error missing source argument
637 8e44e3d4 2025-06-25 xs _source "$(test -f "$1" && printf %s "$PWD/${1##"$PWD"}")"
639 8e44e3d4 2025-06-25 xs nodesource() {
640 8e44e3d4 2025-06-25 xs node "$1" && cd "$(nodedir)" && fsource "$1"
642 8e44e3d4 2025-06-25 xs groupsource() {
643 8e44e3d4 2025-06-25 xs cd "$(groupdir)" && _fsource "$1"
645 87bce7c5 2025-05-30 xs install() ( cd /tmp/r7 && r7_install "$@" ; )
646 87bce7c5 2025-05-30 xs groupinstall() ( cd "$(groupdir)" && r7_install "$@" ; )
647 87bce7c5 2025-05-30 xs nodeinstall() ( cd "$(nodedir)" && r7_install "$@" ; )
649 730e151b 2025-06-02 xs test -f "$1" && {
650 730e151b 2025-06-02 xs ls -l "$1" | grep -v total | awk '{ print $1 " " $3 " " $4 }'
654 730e151b 2025-06-02 xs before=$(_getperm "$1")
655 730e151b 2025-06-02 xs if test -n "$owner"; then
656 730e151b 2025-06-02 xs chown "$owner" "$1" || {
657 730e151b 2025-06-02 xs error chown "$1"
661 730e151b 2025-06-02 xs if test -n "$mode"; then
662 730e151b 2025-06-02 xs chmod "$mode" "$1" || {
663 730e151b 2025-06-02 xs error chmod "$1"
667 730e151b 2025-06-02 xs after=$(_getperm "$1")
668 730e151b 2025-06-02 xs if test "$before" != "$after"; then
669 730e151b 2025-06-02 xs trace PERMISSIONS CHANGE "$before" TO "$after" ON "$1"
673 730e151b 2025-06-02 xs test -e "$2" && {
675 730e151b 2025-06-02 xs diff -U2 "$2" "$1"
677 730e151b 2025-06-02 xs cp "$1" "$2" ||
678 730e151b 2025-06-02 xs { error copying "$source" to "$2"; return 2 ; }
679 730e151b 2025-06-02 xs _setperm "$2"
682 0253bef1 2025-05-29 xs r7_install() (
683 03d8f521 2025-05-27 xs while getopts "o:m:" arg; do
685 730e151b 2025-06-02 xs o) owner=$OPTARG ;;
686 730e151b 2025-06-02 xs m) mode=$OPTARG ;;
690 46924c01 2025-05-28 xs shift $((OPTIND - 1))
693 b7a0ae1e 2025-05-27 xs source_dirname=${1%/*}
694 730e151b 2025-06-02 xs if ! test -e "$source"; then
695 46924c01 2025-05-28 xs error "$source" not found
698 730e151b 2025-06-02 xs case $target in
699 730e151b 2025-06-02 xs */) test -d "$target" || {
700 730e151b 2025-06-02 xs error "$target" directory not found
704 730e151b 2025-06-02 xs if test -e "$target"; then
705 730e151b 2025-06-02 xs cmp -s "$source" "$target"
707 730e151b 2025-06-02 xs destination_is=file
709 46924c01 2025-05-28 xs if test -d "$target"; then
710 46924c01 2025-05-28 xs new_filename="$target/${source##$source_dirname/}"
711 46924c01 2025-05-28 xs cmp -s "$source" "$new_filename"
713 730e151b 2025-06-02 xs destination_is=directory
715 730e151b 2025-06-02 xs if test -e "$source"; then
716 730e151b 2025-06-02 xs if test -d "$target"; then
717 730e151b 2025-06-02 xs destination_is=directory
719 730e151b 2025-06-02 xs destination_is=file
722 46924c01 2025-05-28 xs case "$ret" in
723 46924c01 2025-05-28 xs 0) return 30 ;;
725 730e151b 2025-06-02 xs case $destination_is in
727 730e151b 2025-06-02 xs new_filename="$target/${source##$source_dirname/}"
728 730e151b 2025-06-02 xs _install "$source" "$new_filename"
731 730e151b 2025-06-02 xs _install "$source" "$target"
737 b6cef3b8 2025-06-04 xs installdir() { r7_installdir "$@" ; }
738 f6bdfddc 2025-06-03 xs groupinstalldir() ( cd "$(groupdir)" && r7_installdir "$@" ; )
739 f6bdfddc 2025-06-03 xs nodeinstalldir() ( cd "$(nodedir)" && r7_installdir "$@" ; )
740 730e151b 2025-06-02 xs r7_installdir() (
741 f6bdfddc 2025-06-03 xs while getopts "o:m:O:M:" arg; do
743 730e151b 2025-06-02 xs o) fileowner=$OPTARG ;;
744 730e151b 2025-06-02 xs m) filenode=$OPTARG ;;
745 730e151b 2025-06-02 xs O) dirowner=$OPTARG ;;
746 730e151b 2025-06-02 xs M) dirmode=$OPTARG ;;
750 f6bdfddc 2025-06-03 xs shift $((OPTIND - 1))
753 f6bdfddc 2025-06-03 xs source_basename=${source##*/}
754 f6bdfddc 2025-06-03 xs source_dirname=$PWD/${source%%/*}
755 f6bdfddc 2025-06-03 xs target_basename=${target##*/}
756 f6bdfddc 2025-06-03 xs test -d "$source" || {
757 f6bdfddc 2025-06-03 xs error "$source" directory not found
760 f6bdfddc 2025-06-03 xs for dir in $(find "$source" -type d); do
761 f6bdfddc 2025-06-03 xs newdir=${dir#./}
763 f6bdfddc 2025-06-03 xs cd "$target" || return 2
764 f6bdfddc 2025-06-03 xs mkdir -p "$newdir" || { error unable to create "$newdir"; return 2 ; }
765 f6bdfddc 2025-06-03 xs owner=$dirowner mode=$dirmode _setperm "$1"
768 f6bdfddc 2025-06-03 xs for file in $(find "$source" -type f); do
769 f6bdfddc 2025-06-03 xs newfile=${file#./}
770 f6bdfddc 2025-06-03 xs r7_install -o "$fileowner" -m "$filemode" "$file" "$target/$newfile"
774 6e564797 2025-06-06 xs while getopts "s:" arg; do
776 6e564797 2025-06-06 xs s) source=$OPTARG ;;
780 6e564797 2025-06-06 xs shift $((OPTIND - 1))
782 8e44e3d4 2025-06-25 xs test -n "$source" && sh -n "$source" && set -a && . "./$source" && set +a
784 6e564797 2025-06-06 xs sh > "${file%%.tpl}" <<-.
786 6e564797 2025-06-06 xs $(cat "$file")
791 6e564797 2025-06-06 xs grouptemplate() ( cd "$(groupdir)" && template "$@" ; )
792 6e564797 2025-06-06 xs nodetemplate() ( cd "$(nodedir)" && template "$@" ; )
794 730e151b 2025-06-02 xs cd /tmp/r7 && umask 027
796 730e151b 2025-06-02 xs nodename=$(nodename)
797 730e151b 2025-06-02 xs nodedir=$(nodedir)
798 730e151b 2025-06-02 xs groupdir=$(groupdir)
799 03d8f521 2025-05-27 xs trace CONNECTED $(date +%s)
803 730e151b 2025-06-02 xs _debug "PREFIX $R7_PREFIX"
804 730e151b 2025-06-02 xs for run in $R7_PREFIX; do
805 46924c01 2025-05-28 xs _call "$1" "$run"
808 03d8f521 2025-05-27 xs trace DISCONNECT $(date +%s)
812 03d8f521 2025-05-27 xs # Interactive output function
813 03d8f521 2025-05-27 xs # ===========================
815 46924c01 2025-05-28 xs # shellcheck disable=SC2086,SC2030
818 03d8f521 2025-05-27 xs tee -a "$1" | while read -r line; do
820 03d8f521 2025-05-27 xs case $line in
822 46924c01 2025-05-28 xs # Traces from payload
829 688c9bfd 2025-06-03 xs # Traces from _call
830 03d8f521 2025-05-27 xs case $status in
832 46924c01 2025-05-28 xs save_group=$group save_function=$function
833 03d8f521 2025-05-27 xs in_function=1
834 46924c01 2025-05-28 xs if test "$R7_SHOWOUTPUT" = yes; then
835 03d8f521 2025-05-27 xs echo "- (output) $host:$group::$function"
840 03d8f521 2025-05-27 xs in_function=0
841 03d8f521 2025-05-27 xs case $endcode in
842 730e151b 2025-06-02 xs 0 | 30 | 31) echo "- (ok) $host:$group::$function" ;;
843 03d8f521 2025-05-27 xs *) echo "! (error $endcode) $host:$group::$function" ;;
847 46924c01 2025-05-28 xs # Traces from functions
851 730e151b 2025-06-02 xs SOURCE) echo "+ (source) $host:$save_group::$save_function" ;;
852 46924c01 2025-05-28 xs DIFF) echo "+ (diff) $host:$save_group::$save_function" ;;
853 46924c01 2025-05-28 xs PERMISSIONS) echo "+ (permissions) $host:$save_group::$save_function" ;;
854 46924c01 2025-05-28 xs ERROR) echo "! (error) $host:$save_group::$save_function" ;;
855 730e151b 2025-06-02 xs EXIT) echo "! (exit) $host:$save_group::$save_function" ;;
862 46924c01 2025-05-28 xs if test "$R7_SHOWOUTPUT" = yes; then
863 03d8f521 2025-05-27 xs test "$in_function" -eq 1 &&
864 03d8f521 2025-05-27 xs printf "%s\\n" "$line"
869 03d8f521 2025-05-27 xs # Text output functions
870 03d8f521 2025-05-27 xs # =====================
872 46924c01 2025-05-28 xs alias output-dump=output_dump
873 46924c01 2025-05-28 xs alias outdump=output_dump
874 03d8f521 2025-05-27 xs output_dump() (
875 03d8f521 2025-05-27 xs set -e -- $(output_latest)
876 46924c01 2025-05-28 xs test $# -eq 0 && return 0
877 0253bef1 2025-05-29 xs cd "$R7_WORKDIR" || _fail "Unable to cd to workdir: $R7_WORKDIR"
878 0253bef1 2025-05-29 xs test "$R7_DRYRUN" = yes || {
879 0253bef1 2025-05-29 xs mkdir -p "$R7_OUTPUTDIR"
880 0253bef1 2025-05-29 xs awk -voutput_dir="$R7_OUTPUTDIR" '
886 03d8f521 2025-05-27 xs in_function_block=0
888 03d8f521 2025-05-27 xs /^:: [0-9].*/ {
889 03d8f521 2025-05-27 xs if ($4 == "CONNECTED") {
892 03d8f521 2025-05-27 xs timestamp_connect=$5
893 03d8f521 2025-05-27 xs system("mkdir -p " output_dir "/" hostname)
896 03d8f521 2025-05-27 xs $2 == trace_id && $6 == "START" {
899 03d8f521 2025-05-27 xs in_function_block=1
900 03d8f521 2025-05-27 xs output_node = output_dir "/" hostname "/" groupname "/" funcname
901 03d8f521 2025-05-27 xs output_status = output_node "/status"
902 03d8f521 2025-05-27 xs output_output = output_node "/output"
903 03d8f521 2025-05-27 xs system("rm -fr " output_node)
904 03d8f521 2025-05-27 xs system("mkdir -p " output_node)
907 730e151b 2025-06-02 xs $2 == trace_id && $4 == "EXIT" {
908 730e151b 2025-06-02 xs in_function_block=0
909 730e151b 2025-06-02 xs system("echo " $5 " >" output_status)
911 03d8f521 2025-05-27 xs $2 == trace_id && $6 == "END" {
912 03d8f521 2025-05-27 xs in_function_block=0
913 03d8f521 2025-05-27 xs system("echo " $7 " >" output_status)
915 03d8f521 2025-05-27 xs in_function_block == 1 {
916 03d8f521 2025-05-27 xs print > output_output
918 03d8f521 2025-05-27 xs $2 == trace_id && $4 == "DISCONNECT" {
919 03d8f521 2025-05-27 xs timestamp_disconnect=$5
920 03d8f521 2025-05-27 xs difference=timestamp_disconnect - timestamp_connect
921 03d8f521 2025-05-27 xs output_duration = output_dir "/" hostname "/" groupname "/duration"
922 03d8f521 2025-05-27 xs system("echo " difference " >" output_duration)
929 f6bdfddc 2025-06-03 xs case $(uname) in
930 f6bdfddc 2025-06-03 xs OpenBSD | FreeBSD | NetBSD | Darwin)
934 f6bdfddc 2025-06-03 xs date -d "@$1"
937 f6bdfddc 2025-06-03 xs perl -e "print scalar localtime($1)"
943 46924c01 2025-05-28 xs alias output-latest=output_latest
944 46924c01 2025-05-28 xs alias outlatest=output_latest
945 46924c01 2025-05-28 xs output_latest() (
948 03d8f521 2025-05-27 xs # shellcheck disable=SC2012
949 31e019a2 2025-05-31 xs ls 2>/dev/null -lt1 "$R7_RUNDIR/" | while read -r file; do
950 03d8f521 2025-05-27 xs echo "${file%%.*}"
954 46924c01 2025-05-28 xs test -z "$latest" && return 0
955 f6bdfddc 2025-06-03 xs latest_date=$(_epoch "$latest")
956 03d8f521 2025-05-27 xs if test -n "$latest_date"; then
957 0253bef1 2025-05-29 xs find "$R7_RUNDIR" -type f -name "$latest.*"
961 46924c01 2025-05-28 xs _output_files() (
962 46924c01 2025-05-28 xs cd "$R7_OUTPUTDIR" 2>/dev/null || return 2
963 03d8f521 2025-05-27 xs find ./* -type f -name 'output'
966 46924c01 2025-05-28 xs _status_files() (
967 46924c01 2025-05-28 xs cd "$R7_OUTPUTDIR" 2>/dev/null || return 2
968 03d8f521 2025-05-27 xs find ./* -type f -name 'status'
971 46924c01 2025-05-28 xs _duration_files() (
972 46924c01 2025-05-28 xs cd "$R7_OUTPUTDIR" 2>/dev/null || return 2
973 03d8f521 2025-05-27 xs find ./* -type f -name 'duration'
977 f6bdfddc 2025-06-03 xs column -t 2>/dev/null ||
981 46924c01 2025-05-28 xs alias output-status=output_status
982 46924c01 2025-05-28 xs alias outstat=output_status
983 03d8f521 2025-05-27 xs output_status() (
984 46924c01 2025-05-28 xs cd "$R7_OUTPUTDIR" || return 2
985 f6bdfddc 2025-06-03 xs echo HOST GROUP FUNCTION STATUS
986 46924c01 2025-05-28 xs _status_files | {
987 03d8f521 2025-05-27 xs while read -r status; do
988 03d8f521 2025-05-27 xs echo "$status" | while IFS=/ read -r _ host groupname funcname _; do
989 03d8f521 2025-05-27 xs echo "$host" "$groupname" "$funcname" "$(cat "$status")"
995 46924c01 2025-05-28 xs alias output-duration=output_duration
996 46924c01 2025-05-28 xs alias outdur=output_duration
997 03d8f521 2025-05-27 xs output_duration() (
998 730e151b 2025-06-02 xs cd "$R7_OUTPUTDIR" || return 2
999 f6bdfddc 2025-06-03 xs echo HOST GROUP DURATION
1000 46924c01 2025-05-28 xs _duration_files | {
1001 03d8f521 2025-05-27 xs while read -r duration; do
1002 03d8f521 2025-05-27 xs echo "$duration" | while IFS=/ read -r _ host groupname _; do
1003 03d8f521 2025-05-27 xs echo "$host" "$groupname" "$(cat "$duration")"
1009 46924c01 2025-05-28 xs alias out=output
1010 87bce7c5 2025-05-30 xs # shellcheck disable=SC2254
1012 730e151b 2025-06-02 xs cd "$R7_OUTPUTDIR" || return 2
1013 46924c01 2025-05-28 xs _output_files | while read -r output; do
1014 03d8f521 2025-05-27 xs echo "$output" | while IFS=/ read -r _ host groupname funcname _; do
1015 87bce7c5 2025-05-30 xs case $host in ${1:-*})
1016 87bce7c5 2025-05-30 xs case $groupname in ${2:-*})
1017 87bce7c5 2025-05-30 xs case $funcname in ${3:-*})
1018 87bce7c5 2025-05-30 xs echo "- $host" "$groupname" "$funcname"
1020 87bce7c5 2025-05-30 xs sed 's/^/ /' "$R7_OUTPUTDIR/$output"
1032 46924c01 2025-05-28 xs alias trace-id-last=trace_id_last
1033 46924c01 2025-05-28 xs alias lastid=trace_id_last
1034 46924c01 2025-05-28 xs trace_id_last() {
1035 46924c01 2025-05-28 xs output_latest | while read -r output; do
1036 46924c01 2025-05-28 xs output=${output##*/}
1037 46924c01 2025-05-28 xs output=${output%%.*}
1038 46924c01 2025-05-28 xs echo "$output"
1043 46924c01 2025-05-28 xs # _output/tools0/r7test/r7test_diff/output::: 1748408055 tools0 DIFF /tmp/difftest r7test.dir/difftest
1044 46924c01 2025-05-28 xs alias output-diffs=output_diffs
1045 46924c01 2025-05-28 xs alias outdiffs=output_diffs
1046 46924c01 2025-05-28 xs # shellcheck disable=SC2048,SC2086
1047 46924c01 2025-05-28 xs output_diffs() (
1048 46924c01 2025-05-28 xs trace_id=$(trace_id_last)
1049 0253bef1 2025-05-29 xs cd "$R7_OUTPUTDIR" || return 0
1050 730e151b 2025-06-02 xs grep -r "^:: ${trace_id:-} .* DIFF" | uniq | while read -r line; do
1051 46924c01 2025-05-28 xs set -- $line
1052 46924c01 2025-05-28 xs output_file=${1%%:::}
1055 46924c01 2025-05-28 xs set -- $output_file
1056 0253bef1 2025-05-29 xs host=$1 groupname=$2 funcname=$3
1058 f83445b1 2025-05-30 xs echo "- (diff) -" "in" "$host" "$groupname" "$funcname" "($trace_id)"
1060 46924c01 2025-05-28 xs sed 's/^/ /' "$R7_OUTPUTDIR/$host/$groupname/$funcname/output" | grep -v "^ :: $trace_id $host"
1062 730e151b 2025-06-02 xs done || return 0
1066 46924c01 2025-05-28 xs # :: 1748415578 tools0 PERMISSIONS CHANGE -rw-r-xr-x root wheel -> -rw-r--r-- root wheel on /tmp/difftest
1067 46924c01 2025-05-28 xs alias output-permissions=output_permissions
1068 46924c01 2025-05-28 xs alias outperm=output_permissions
1069 46924c01 2025-05-28 xs # shellcheck disable=SC2048,SC2086
1070 46924c01 2025-05-28 xs output_permissions() (
1071 46924c01 2025-05-28 xs trace_id=$(trace_id_last)
1072 730e151b 2025-06-02 xs cd "$R7_OUTPUTDIR" || return 0
1073 730e151b 2025-06-02 xs grep -r "^:: ${trace_id:-} .* PERMISSIONS" | while read -r line; do
1074 46924c01 2025-05-28 xs set -- $line
1075 46924c01 2025-05-28 xs output_file=${1%%:::}
1078 46924c01 2025-05-28 xs set -- $output_file
1079 730e151b 2025-06-02 xs host=$1 groupname=$2 funcname=$3
1081 f83445b1 2025-05-30 xs echo "- (permissions) -" "in" "$host" "$groupname" "$funcname" "($trace_id)"
1083 730e151b 2025-06-02 xs grep -e "^:: ${trace_id:-} .* PERMISSIONS CHANGE" "$R7_OUTPUTDIR/$host/$groupname/$funcname/output" |
1084 730e151b 2025-06-02 xs while read -r _ _ _ _ _ mode owner group _ mode2 owner2 group2 _ filename; do
1085 730e151b 2025-06-02 xs echo " - $filename from $mode $owner:$group to $mode2 $owner2:$group2"
1092 46924c01 2025-05-28 xs alias output-errors=output_errors
1093 46924c01 2025-05-28 xs alias outerr=output_errors
1094 46924c01 2025-05-28 xs output_errors() (
1095 f83445b1 2025-05-30 xs trace_id=$(trace_id_last)
1096 f6bdfddc 2025-06-03 xs output_status | while read -r host groupname funcname status; do
1097 b7a0ae1e 2025-05-27 xs case $status in
1098 f6bdfddc 2025-06-03 xs STATUS | 0 | 30 | 31) continue ;;
1100 f83445b1 2025-05-30 xs echo "- (error $status) -" "in" "$host" "$groupname" "$funcname" "($trace_id)"
1101 46924c01 2025-05-28 xs if test -f "$R7_OUTPUTDIR/$host/$groupname/$funcname/output"; then
1103 46924c01 2025-05-28 xs sed 's/^/ /' "$R7_OUTPUTDIR/$host/$groupname/$funcname/output"
1111 0253bef1 2025-05-29 xs test -d "$R7_OUTPUTDIR" || return 0
1112 0253bef1 2025-05-29 xs summary_out=$(
1113 0253bef1 2025-05-29 xs output_errors
1114 0253bef1 2025-05-29 xs output_diffs
1115 0253bef1 2025-05-29 xs output_permissions
1117 0253bef1 2025-05-29 xs test -n "$summary_out" &&
1118 0253bef1 2025-05-29 xs printf "%s\n" "$summary_out"
1122 03d8f521 2025-05-27 xs # Digraph functions
1123 03d8f521 2025-05-27 xs # =================
1125 03d8f521 2025-05-27 xs # Used in the digraph generation because inside a heredoc
1126 03d8f521 2025-05-27 xs # context, memebers don't work as expected (OKSH).
1127 46924c01 2025-05-28 xs # shellcheck disable=SC2048,SC2086
1128 46924c01 2025-05-28 xs _members2() (
1131 03d8f521 2025-05-27 xs nodes | grep -e '.*'
1135 03d8f521 2025-05-27 xs set -f -- $(printf -- "-e %s " "$@")
1136 46924c01 2025-05-28 xs nodes | grep $*
1139 46924c01 2025-05-28 xs alias digraph-host=digraph_host
1140 46924c01 2025-05-28 xs # shellcheck disable=SC2086
1141 03d8f521 2025-05-27 xs digraph_host() (
1142 46924c01 2025-05-28 xs _debug "DIGRAPH HOST"
1144 03d8f521 2025-05-27 xs digraph host {
1145 03d8f521 2025-05-27 xs layout="dot"
1146 03d8f521 2025-05-27 xs splines="true"
1147 03d8f521 2025-05-27 xs graph [overlap="false", rankdir=LR, mode=KK]
1150 03d8f521 2025-05-27 xs for node in $(nodes); do echo "\"$node\" [peripheries=2 color=blue]"; done
1151 46924c01 2025-05-28 xs grep 2>/dev/null "^group" "$R7_SITE_CONFIG" | while read -r _ groupname nodes; do
1152 03d8f521 2025-05-27 xs echo "\"$groupname\" [color=orange]"
1153 46924c01 2025-05-28 xs for node in $(_members2 $nodes); do
1154 03d8f521 2025-05-27 xs echo "\"$node\" -> \"$groupname\""
1156 46924c01 2025-05-28 xs for funct in $(group_functions "$groupname"); do
1157 730e151b 2025-06-02 xs echo "\"$groupname::$funct\" [color=red]"
1158 730e151b 2025-06-02 xs echo "\"$groupname\" -> \"$groupname::$funct\""
1166 f83445b1 2025-05-30 xs alias digraph-ssh-id-hosts=digraph_ssh_id_hosts
1167 f83445b1 2025-05-30 xs digraph_ssh_id_hosts() {
1169 730e151b 2025-06-02 xs digraph ssh_id_hosts {
1170 730e151b 2025-06-02 xs layout="dot"
1171 730e151b 2025-06-02 xs splines="true"
1172 730e151b 2025-06-02 xs graph [overlap="false"]
1173 730e151b 2025-06-02 xs $(ssh_ids_hosts)
1182 6e564797 2025-06-06 xs # shellcheck disable=SC2120
1184 730e151b 2025-06-02 xs pandoc -t html -f markdown+smart "$@"
1188 8fe13599 2025-06-05 xs output \* \* doc\* | grep ^- | while read -r _ host group function; do
1189 8fe13599 2025-06-05 xs cat "$R7_OUTPUTDIR/$host/$group/$function/output"
1194 8fe13599 2025-06-05 xs _summary() {
1195 0551e9c2 2025-06-04 xs summary | while read -r line; do
1196 0551e9c2 2025-06-04 xs set -- $line
1199 0551e9c2 2025-06-04 xs printf %s\\n "$line"
1201 0551e9c2 2025-06-04 xs *) continue ;;
1205 0551e9c2 2025-06-04 xs cat "$R7_OUTPUTDIR/$6/$7/$8/output"
1213 688c9bfd 2025-06-03 xs <!DOCTYPE html>
1214 688c9bfd 2025-06-03 xs <html lang="en">
1216 4459883d 2025-06-05 xs <meta charset="UTF-8">
1217 4459883d 2025-06-05 xs <meta name="viewport" content="width=device-width,initial-scale=1">
1218 4459883d 2025-06-05 xs <title>${PWD##*/}</title>
1220 4459883d 2025-06-05 xs body{margin:1rem;background:#f5f7fa;color:#333;font:14px/1.5 Consolas,monospace}
1221 4459883d 2025-06-05 xs .container{max-width:800px;margin:0 auto;padding:1.5rem;background:#fff;box-shadow:0 1px 4px rgba(0,0,0,0.05);border-radius:3px}
1222 4459883d 2025-06-05 xs header{border-bottom:1px solid #ddd;margin-bottom:1rem;padding-bottom:.5rem}
1223 4459883d 2025-06-05 xs header h1{margin:0;font-size:1.4rem;font-weight:400}
1224 4459883d 2025-06-05 xs header time{font-size:.85rem;color:#666}
1225 4459883d 2025-06-05 xs .summary{border-bottom:1px dashed #eee;padding:.5rem 0}
1226 4459883d 2025-06-05 xs .summary:last-child{border:none;padding:0}
1227 4459883d 2025-06-05 xs .timestamp{display:block;font-size:.8rem;color:#555;margin-bottom:.25rem}
1231 4459883d 2025-06-05 xs <div class="container">
1233 4459883d 2025-06-05 xs <h1>Summary</h1>
1235 4459883d 2025-06-05 xs <section class="summary">
1236 4459883d 2025-06-05 xs $(_summary | md)
1238 4459883d 2025-06-05 xs <section class="summary">
1239 4459883d 2025-06-05 xs $(_doc | md)
1252 d8747c12 2025-06-03 xs cd "$R7_WORKDIR" || _fail "Unable to cd to '$R7_WORKDIR'"
1254 a4759196 2025-06-13 xs if test -n "$target"; then
1255 a4759196 2025-06-13 xs echo >&2 "-> Importing target host: $target"
1256 a4759196 2025-06-13 xs case $(ssh "$target" uname) in
1257 a4759196 2025-06-13 xs Linux) _Linux_import ;;
1258 a4759196 2025-06-13 xs SunOS) _SunOS_import ;;
1259 a4759196 2025-06-13 xs OpenBSD) _OpenBSD_import ;;
1260 a4759196 2025-06-13 xs FreeBSD) _FreeBSD_import ;;
1261 a4759196 2025-06-13 xs NetBSD) _NetBSD_import ;;
1262 a4759196 2025-06-13 xs DragonFly) _DragonFly_import ;;
1263 8e44e3d4 2025-06-25 xs Haiku) _Haiku_import ;;
1264 8e44e3d4 2025-06-25 xs QNX) _QNX_import ;;
1265 8e44e3d4 2025-06-25 xs Minix) _Minix_import ;;
1267 a4759196 2025-06-13 xs _error "Platform unsupported or host unavailable."
1271 a4759196 2025-06-13 xs echo >&2 "-> done: $target"
1273 a4759196 2025-06-13 xs echo >&2 "usage: $0 target-host"
1277 a4759196 2025-06-13 xs _import_copy() (
1280 a4759196 2025-06-13 xs _debug "COPY $src $dst"
1281 a4759196 2025-06-13 xs ssh "$target" command -v rsync >/dev/null && {
1282 a4759196 2025-06-13 xs _debug "IMPORT $1 $2 USE RSYNC"
1283 622e6949 2025-06-13 xs rsync -e "/usr/bin/ssh -oConnectTimeout=$SSH_CONNECT_TIMEOUT -F $SSH_CONFIG_FILE" \
1284 622e6949 2025-06-13 xs -a "$src" "$dst" && return 0
1286 a4759196 2025-06-13 xs _openrsync=$(ssh "$target" command -v openrsync) && {
1287 a4759196 2025-06-13 xs _debug "IMPORT $1 $2 USE OPENRSYNC"
1288 a4759196 2025-06-13 xs openrsync -e "/usr/bin/ssh -oConnectTimeout=$SSH_CONNECT_TIMEOUT -F $SSH_CONFIG_FILE" \
1289 a4759196 2025-06-13 xs --rsync-path="$_openrsync" \
1290 a4759196 2025-06-13 xs -a "$src" "$dst" && return 0
1292 a4759196 2025-06-13 xs _debug "IMPORT $1 $2 USE SCP"
1293 8e44e3d4 2025-06-25 xs scp "-oConnectTimeout=$SSH_CONNECT_TIMEOUT" -qF "$SSH_CONFIG_FILE" -r "$src" "$dst" && return 0
1294 a4759196 2025-06-13 xs _fail "IMPORT $1 $2 FAILED"
1297 8e44e3d4 2025-06-25 xs _import_ssh_authorized_keys_old() (
1299 a4759196 2025-06-13 xs other_users=$(
1300 a4759196 2025-06-13 xs ssh "$target" getent passwd | awk -F: '$3 >= 1000 { print $1 }' |
1301 a4759196 2025-06-13 xs grep -v nobody
1304 a4759196 2025-06-13 xs for user in root $other_users; do
1305 a4759196 2025-06-13 xs # shellcheck disable=SC2029
1306 a4759196 2025-06-13 xs home=$(ssh "$target" getent passwd "$user" | awk -F: '{ print $6 }')
1307 a4759196 2025-06-13 xs authorized_keys="$home/.ssh/authorized_keys"
1308 a4759196 2025-06-13 xs ssh "$target" test -f "$authorized_keys" || continue
1309 a4759196 2025-06-13 xs mkdir -p "nodes/$target/ssh/"
1310 a4759196 2025-06-13 xs echo >&2 "--> Importing $authorized_keys"
1311 8e44e3d4 2025-06-25 xs _import_copy "$target:$authorized_keys" "nodes/$target/ssh/authorized_keys_$user"
1315 8e44e3d4 2025-06-25 xs _import_ssh_authorized_keys() (
1317 8e44e3d4 2025-06-25 xs other_users=$(
1318 8e44e3d4 2025-06-25 xs ssh "$target" cat /etc/passwd | awk -F: '$3 >= 1000 { print $1 }' | grep -v nobody
1320 8e44e3d4 2025-06-25 xs for user in root $other_users; do
1322 8e44e3d4 2025-06-25 xs ssh "$target" cat /etc/passwd | awk -F: -v user="$user" '$1 == user { print $6 }'
1324 8e44e3d4 2025-06-25 xs authorized_keys="$home/.ssh/authorized_keys"
1325 8e44e3d4 2025-06-25 xs if ! ssh -n "$target" test -f "$authorized_keys"; then
1328 8e44e3d4 2025-06-25 xs mkdir -p "nodes/$target/ssh/"
1329 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing $authorized_keys for user='$user'"
1330 8e44e3d4 2025-06-25 xs _import_copy "$target:$authorized_keys" "nodes/$target/ssh/authorized_keys_$user"
1334 a4759196 2025-06-13 xs _import_etc_if_exists() {
1335 a4759196 2025-06-13 xs mkdir -p nodes/"$target"
1336 a4759196 2025-06-13 xs echo >&2 "--> Importing /etc"
1337 a4759196 2025-06-13 xs for file in $etc_files_to_import; do
1338 a4759196 2025-06-13 xs ssh "$target" test -f "'/etc/$file'" &&
1339 a4759196 2025-06-13 xs _import_copy "$target:/etc/$file" "nodes/$target"
1343 a4759196 2025-06-13 xs _import_etc_ssh_if_exists() {
1344 8e44e3d4 2025-06-25 xs etc_ssh_files_to_import='
1345 8e44e3d4 2025-06-25 xs ssh/sshd_config
1346 8e44e3d4 2025-06-25 xs ssh/ssh_config
1348 8e44e3d4 2025-06-25 xs mkdir -p nodes/"$target/ssh"
1349 a4759196 2025-06-13 xs echo >&2 "--> Importing /etc/ssh"
1350 a4759196 2025-06-13 xs for file in $etc_ssh_files_to_import; do
1351 a4759196 2025-06-13 xs ssh "$target" test -f "'/etc/$file'" &&
1352 a4759196 2025-06-13 xs _import_copy "$target:/etc/$file" "nodes/$target/ssh"
1356 a4759196 2025-06-13 xs _import_modprobe() {
1357 8e44e3d4 2025-06-25 xs if ssh "$target" test -d /etc/modprobe.d; then
1358 8e44e3d4 2025-06-25 xs _content=$(ssh "$target" ls -A /etc/modprobe.d/)
1359 8e44e3d4 2025-06-25 xs test -z "$_content" && return 0
1360 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /etc/modprobe.d"
1361 8e44e3d4 2025-06-25 xs mkdir -p nodes/"$target/modprobe.d"
1362 8e44e3d4 2025-06-25 xs _import_copy "$target:/etc/modprobe.d/*" "nodes/$target/modprobe.d"
1366 8e44e3d4 2025-06-25 xs _QNX_import() {
1367 8e44e3d4 2025-06-25 xs etc_files_to_import='
1370 8e44e3d4 2025-06-25 xs _import_etc_if_exists
1371 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /etc/settings"
1372 8e44e3d4 2025-06-25 xs mkdir -p "nodes/$target/settings"
1373 8e44e3d4 2025-06-25 xs _import_copy "$target:/etc/settings" "nodes/$target/settings"
1374 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /system/etc/ssh"
1375 8e44e3d4 2025-06-25 xs mkdir -p "nodes/$target/ssh"
1376 8e44e3d4 2025-06-25 xs _import_copy "$target:/system/etc/ssh/sshd_config" "nodes/$target/ssh"
1377 8e44e3d4 2025-06-25 xs _import_ssh_authorized_keys
1380 8e44e3d4 2025-06-25 xs _Minix_import() {
1381 8e44e3d4 2025-06-25 xs etc_files_to_import='
1389 8e44e3d4 2025-06-25 xs _import_etc_if_exists
1390 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /usr/pkg/etc/ssh"
1391 8e44e3d4 2025-06-25 xs mkdir -p nodes/"$target/ssh"
1392 8e44e3d4 2025-06-25 xs _import_copy "$target:/usr/pkg/etc/ssh/sshd_config" "nodes/$target/ssh"
1393 8e44e3d4 2025-06-25 xs _import_copy "$target:/usr/pkg/etc/ssh/ssh_config" "nodes/$target/ssh"
1394 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /usr/pkg/etc/pkgin"
1395 8e44e3d4 2025-06-25 xs mkdir -p nodes/"$target/pkgin"
1396 8e44e3d4 2025-06-25 xs _import_copy "$target:/usr/pkg/etc/pkgin/repositories.conf" "nodes/$target/pkgin"
1399 8e44e3d4 2025-06-25 xs _Haiku_import() {
1400 8e44e3d4 2025-06-25 xs etc_files_to_import='
1404 8e44e3d4 2025-06-25 xs _import_etc_if_exists
1405 8e44e3d4 2025-06-25 xs _import_copy "$target:/boot/system/settings/network/hosts" "nodes/$target/"
1406 8e44e3d4 2025-06-25 xs _import_copy "$target:/boot/system/settings/network/resolv.conf" "nodes/$target/"
1407 8e44e3d4 2025-06-25 xs _import_copy "$target:/boot/system/settings/package-repositories" "nodes/$target/"
1408 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /boot/system/settings/ssh"
1409 8e44e3d4 2025-06-25 xs mkdir -p "nodes/$target/ssh"
1410 8e44e3d4 2025-06-25 xs _import_copy "$target:/boot/system/settings/ssh/sshd_config" "nodes/$target/ssh"
1411 8e44e3d4 2025-06-25 xs _import_copy "$target:/boot/system/settings/ssh/ssh_config" "nodes/$target/ssh"
1412 8e44e3d4 2025-06-25 xs _import_copy "$target:/boot/home/config/settings/ssh/authorized_keys" \
1413 8e44e3d4 2025-06-25 xs "nodes/$target/ssh/authorized_keys_user"
1416 a4759196 2025-06-13 xs _Linux_import() {
1417 a4759196 2025-06-13 xs if _Linux_is_debian_based; then
1418 a4759196 2025-06-13 xs _Linux_import_debian
1419 a4759196 2025-06-13 xs elif _Linux_is_rhel_based; then
1420 a4759196 2025-06-13 xs _Linux_import_rhel
1421 8e44e3d4 2025-06-25 xs elif _Linux_is_suse_based; then
1422 8e44e3d4 2025-06-25 xs _Linux_import_suse
1423 a4759196 2025-06-13 xs elif _Linux_is_gentoo_based; then
1424 a4759196 2025-06-13 xs _Linux_import_gentoo
1425 8e44e3d4 2025-06-25 xs elif _Linux_is_arch_based; then
1426 8e44e3d4 2025-06-25 xs _Linux_import_arch
1427 8e44e3d4 2025-06-25 xs elif _Linux_is_artix; then
1428 8e44e3d4 2025-06-25 xs _Linux_import_artix
1429 a4759196 2025-06-13 xs elif ssh "$target" "grep -q 'ID=alpine' /etc/os-release"; then
1430 a4759196 2025-06-13 xs _Linux_import_alpine
1431 8e44e3d4 2025-06-25 xs elif ssh "$target" "grep -q 'ID=crux' /etc/os-release"; then
1432 8e44e3d4 2025-06-25 xs _Linux_import_crux
1433 8e44e3d4 2025-06-25 xs elif ssh "$target" "grep -q 'ID=\"void\"' /etc/os-release"; then
1434 8e44e3d4 2025-06-25 xs _Linux_import_void
1435 8e44e3d4 2025-06-25 xs elif ssh "$target" "grep -q 'ID=guix' /etc/os-release"; then
1436 8e44e3d4 2025-06-25 xs _Linux_import_guix
1438 a4759196 2025-06-13 xs echo >&2 "Unsupported Linux distribution."
1442 a4759196 2025-06-13 xs _Linux_is_debian_based() (
1443 8e44e3d4 2025-06-25 xs ssh "$target" "grep -q 'ID=debian' /etc/os-release || \
1444 8e44e3d4 2025-06-25 xs grep -q 'ID_LIKE=.*debian' /etc/os-release || \
1445 8e44e3d4 2025-06-25 xs grep -qiE 'Debian|Ubuntu|Linux Mint' /etc/issue || \
1446 8e44e3d4 2025-06-25 xs test -f /etc/debian_version || test -d /etc/apt || command -v apt-get >/dev/null 2>&1" 2>/dev/null
1449 a4759196 2025-06-13 xs _Linux_is_rhel_based() (
1450 8e44e3d4 2025-06-25 xs ssh "$target" "grep -qiE 'ID=\"?(rhel|fedora|centos|ol|scientific|rocky|eurolinux)\"?' /etc/os-release || \
1451 8e44e3d4 2025-06-25 xs grep -qiE 'ID_LIKE=\"?.*(rhel|fedora).*\"?' /etc/os-release || \
1452 8e44e3d4 2025-06-25 xs grep -qiE 'Red Hat|CentOS|Fedora|Scientific Linux|Oracle Linux' /etc/issue || \
1453 8e44e3d4 2025-06-25 xs test -f /etc/redhat-release || test -f /etc/system-release || test -f /etc/centos-release || \
1454 8e44e3d4 2025-06-25 xs test -f /etc/fedora-release || test -f /etc/oracle-release || \
1455 8e44e3d4 2025-06-25 xs (test -f /etc/os-release && grep -qi 'rhel' /etc/os-release) || test -d /etc/yum.repos.d" 2>/dev/null
1458 a4759196 2025-06-13 xs _Linux_is_gentoo_based() (
1459 a4759196 2025-06-13 xs ssh "$target" "test -f /etc/gentoo-release || \
1460 8e44e3d4 2025-06-25 xs grep -q 'ID=gentoo' /etc/os-release || \
1461 8e44e3d4 2025-06-25 xs grep -q 'ID=funtoo' /etc/os-release || \
1462 8e44e3d4 2025-06-25 xs grep -qi 'Gentoo' /etc/issue || \
1463 8e44e3d4 2025-06-25 xs command -v emerge >/dev/null 2>&1" 2>/dev/null
1466 8e44e3d4 2025-06-25 xs _Linux_is_suse_based() (
1467 8e44e3d4 2025-06-25 xs ssh "$target" "test -f /etc/os-release && grep -Eqi \
1468 8e44e3d4 2025-06-25 xs 'suse|opensuse' /etc/os-release || \
1469 8e44e3d4 2025-06-25 xs command -v zypper >/dev/null || \
1470 8e44e3d4 2025-06-25 xs command -v yast2 >/dev/null" 2>/dev/null
1473 8e44e3d4 2025-06-25 xs _Linux_is_arch_based() (
1474 8e44e3d4 2025-06-25 xs ssh "$target" "test -f /etc/os-release && grep -Eqi \
1475 8e44e3d4 2025-06-25 xs 'arch|manjaro|endeavouros|arcolinux|parabola' /etc/os-release || \
1476 8e44e3d4 2025-06-25 xs command -v pacman >/dev/null || \
1477 8e44e3d4 2025-06-25 xs command -v makepkg >/dev/null" 2>/dev/null
1480 8e44e3d4 2025-06-25 xs _Linux_is_artix() (
1481 8e44e3d4 2025-06-25 xs ssh "$target" "test -f /etc/os-release && grep -Eqi \
1482 8e44e3d4 2025-06-25 xs 'artix|hyperbola' /etc/os-release || \
1483 8e44e3d4 2025-06-25 xs command -v pacman >/dev/null || \
1484 8e44e3d4 2025-06-25 xs command -v makepkg >/dev/null" 2>/dev/null
1487 a4759196 2025-06-13 xs _Linux_import_gentoo() {
1488 730e151b 2025-06-02 xs etc_files_to_import='
1500 a4759196 2025-06-13 xs nsswitch.conf
1508 a4759196 2025-06-13 xs _import_etc_if_exists
1509 a4759196 2025-06-13 xs _import_etc_ssh_if_exists
1510 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /etc/portage"
1511 8e44e3d4 2025-06-25 xs mkdir -p "nodes/$target/portage"
1512 8e44e3d4 2025-06-25 xs _import_copy "$target:/etc/portage/" "nodes/$target/portage/"
1513 a4759196 2025-06-13 xs _import_ssh_authorized_keys
1514 a4759196 2025-06-13 xs _import_modprobe
1517 a4759196 2025-06-13 xs _Linux_import_alpine() {
1518 a4759196 2025-06-13 xs etc_files_to_import='
1528 a4759196 2025-06-13 xs nsswitch.conf
1536 a4759196 2025-06-13 xs _import_etc_if_exists
1537 a4759196 2025-06-13 xs _import_etc_ssh_if_exists
1538 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /etc/apk"
1539 8e44e3d4 2025-06-25 xs mkdir -p "nodes/$target/apk"
1540 8e44e3d4 2025-06-25 xs _import_copy "$target:/etc/apk/repositories" "nodes/$target/apk/"
1541 a4759196 2025-06-13 xs _import_ssh_authorized_keys
1542 a4759196 2025-06-13 xs _import_modprobe
1545 a4759196 2025-06-13 xs _Linux_import_rhel() {
1546 a4759196 2025-06-13 xs etc_files_to_import='
1557 a4759196 2025-06-13 xs nsswitch.conf
1565 a4759196 2025-06-13 xs _import_etc_if_exists
1566 a4759196 2025-06-13 xs _import_etc_ssh_if_exists
1567 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /etc/yum.repos.d"
1568 8e44e3d4 2025-06-25 xs mkdir -p "nodes/$target/yum.repos.d"
1569 8e44e3d4 2025-06-25 xs _import_copy "$target:/etc/yum.repos.d/" "nodes/$target/yum.repos.d/"
1570 a4759196 2025-06-13 xs _import_ssh_authorized_keys
1571 a4759196 2025-06-13 xs _import_modprobe
1574 a4759196 2025-06-13 xs _Linux_import_debian() {
1575 a4759196 2025-06-13 xs etc_files_to_import='
1585 a4759196 2025-06-13 xs nsswitch.conf
1593 a4759196 2025-06-13 xs _import_etc_if_exists
1594 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /etc/apt"
1595 8e44e3d4 2025-06-25 xs mkdir -p "nodes/$target/apt"
1596 8e44e3d4 2025-06-25 xs _import_copy "$target:/etc/apt/sources.list" "nodes/$target/apt"
1597 8e44e3d4 2025-06-25 xs _import_copy "$target:/etc/apt/sources.list.d" "nodes/$target/apt"
1598 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /etc/network"
1599 8e44e3d4 2025-06-25 xs mkdir -p "nodes/$target/network/"
1600 8e44e3d4 2025-06-25 xs _import_copy "$target:/etc/network/interfaces" "nodes/$target/network/"
1601 8e44e3d4 2025-06-25 xs _import_copy "$target:/etc/network/interfaces.d" "nodes/$target/network/"
1602 a4759196 2025-06-13 xs _import_etc_ssh_if_exists
1603 a4759196 2025-06-13 xs _import_ssh_authorized_keys
1604 a4759196 2025-06-13 xs _import_modprobe
1608 8e44e3d4 2025-06-25 xs _Linux_import_suse() {
1609 8e44e3d4 2025-06-25 xs etc_files_to_import='
1620 8e44e3d4 2025-06-25 xs _import_etc_if_exists
1621 8e44e3d4 2025-06-25 xs _import_etc_ssh_if_exists
1622 8e44e3d4 2025-06-25 xs _import_ssh_authorized_keys
1623 8e44e3d4 2025-06-25 xs _import_modprobe
1626 8e44e3d4 2025-06-25 xs _Linux_import_arch() {
1627 8e44e3d4 2025-06-25 xs etc_files_to_import='
1631 8e44e3d4 2025-06-25 xs arch-release
1639 8e44e3d4 2025-06-25 xs _import_etc_if_exists
1640 8e44e3d4 2025-06-25 xs _import_etc_ssh_if_exists
1641 8e44e3d4 2025-06-25 xs _import_ssh_authorized_keys
1642 8e44e3d4 2025-06-25 xs _import_modprobe
1645 8e44e3d4 2025-06-25 xs _Linux_import_artix() {
1646 8e44e3d4 2025-06-25 xs _Linux_import_arch
1649 8e44e3d4 2025-06-25 xs _Linux_import_guix() {
1650 8e44e3d4 2025-06-25 xs etc_files_to_import='
1657 8e44e3d4 2025-06-25 xs nsswitch.conf
1663 8e44e3d4 2025-06-25 xs _import_etc_if_exists
1664 8e44e3d4 2025-06-25 xs _import_etc_ssh_if_exists
1665 8e44e3d4 2025-06-25 xs _import_ssh_authorized_keys
1668 8e44e3d4 2025-06-25 xs _Linux_import_void() {
1669 8e44e3d4 2025-06-25 xs etc_files_to_import='\
1676 8e44e3d4 2025-06-25 xs nsswitch.conf
1683 8e44e3d4 2025-06-25 xs _import_etc_if_exists
1684 8e44e3d4 2025-06-25 xs _import_etc_ssh_if_exists
1685 8e44e3d4 2025-06-25 xs _import_ssh_authorized_keys
1686 8e44e3d4 2025-06-25 xs _import_modprobe
1689 8e44e3d4 2025-06-25 xs _Linux_import_crux() {
1690 8e44e3d4 2025-06-25 xs etc_files_to_import='
1704 8e44e3d4 2025-06-25 xs _import_etc_if_exists
1705 8e44e3d4 2025-06-25 xs _import_etc_ssh_if_exists
1706 8e44e3d4 2025-06-25 xs _import_ssh_authorized_keys
1707 8e44e3d4 2025-06-25 xs _import_modprobe
1710 a4759196 2025-06-13 xs _SunOS_import() {
1711 a4759196 2025-06-13 xs etc_files_to_import='
1722 a4759196 2025-06-13 xs nsswitch.conf
1727 a4759196 2025-06-13 xs rsyslog.conf
1732 a4759196 2025-06-13 xs _import_etc_if_exists
1733 a4759196 2025-06-13 xs _import_etc_ssh_if_exists
1734 a4759196 2025-06-13 xs _import_ssh_authorized_keys
1737 8e44e3d4 2025-06-25 xs _FreeBSD_import() {
1738 8e44e3d4 2025-06-25 xs etc_files_to_import='
1745 8e44e3d4 2025-06-25 xs nsswitch.conf
1753 8e44e3d4 2025-06-25 xs _import_etc_if_exists
1754 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /etc/pkg"
1755 8e44e3d4 2025-06-25 xs _import_copy "$target:/etc/pkg" "nodes/$target/"
1756 8e44e3d4 2025-06-25 xs _import_etc_ssh_if_exists
1757 8e44e3d4 2025-06-25 xs _import_ssh_authorized_keys
1760 8e44e3d4 2025-06-25 xs _DragonFly_import() {
1761 8e44e3d4 2025-06-25 xs etc_files_to_import='
1772 8e44e3d4 2025-06-25 xs _import_etc_if_exists
1773 8e44e3d4 2025-06-25 xs _import_etc_ssh_if_exists
1774 8e44e3d4 2025-06-25 xs _import_ssh_authorized_keys
1777 8e44e3d4 2025-06-25 xs _NetBSD_import() {
1778 8e44e3d4 2025-06-25 xs etc_files_to_import='
1787 8e44e3d4 2025-06-25 xs usermgmt.conf
1791 8e44e3d4 2025-06-25 xs _import_etc_if_exists
1792 8e44e3d4 2025-06-25 xs _import_etc_ssh_if_exists
1793 8e44e3d4 2025-06-25 xs _import_ssh_authorized_keys
1796 a4759196 2025-06-13 xs _OpenBSD_import() {
1797 a4759196 2025-06-13 xs etc_files_to_import='
1798 730e151b 2025-06-02 xs acme-client.conf
1805 a4759196 2025-06-13 xs gotwebd.conf
1813 a4759196 2025-06-13 xs newsyslog.conf
1820 a4759196 2025-06-13 xs usermgt.conf
1821 8e44e3d4 2025-06-25 xs rc.conf.local
1823 730e151b 2025-06-02 xs etc_ssh_files_to_import='
1824 730e151b 2025-06-02 xs ssh/sshd_config
1825 730e151b 2025-06-02 xs ssh/ssh_config
1827 730e151b 2025-06-02 xs var_unbound_files='
1828 730e151b 2025-06-02 xs db/named.root
1829 730e151b 2025-06-02 xs etc/unbound.conf
1831 730e151b 2025-06-02 xs var_nsd_files='
1832 730e151b 2025-06-02 xs etc/nsd.conf
1834 730e151b 2025-06-02 xs var_postgresql_files='
1835 730e151b 2025-06-02 xs data/pg_hba.conf
1836 730e151b 2025-06-02 xs data/postgresql.conf
1838 a4759196 2025-06-13 xs _import_ssh_authorized_keys
1839 a4759196 2025-06-13 xs _OpenBSD_import_etc_if_exists
1840 a4759196 2025-06-13 xs _import_etc_ssh_if_exists
1841 a4759196 2025-06-13 xs _OpenBSD_import_etc_mail_if_exists
1842 a4759196 2025-06-13 xs _OpenBSD_import_unbound_if_enabled
1843 a4759196 2025-06-13 xs _OpenBSD_import_nsd_if_enabled
1844 a4759196 2025-06-13 xs _OpenBSD_import_postgresql_if_installed
1847 d8747c12 2025-06-03 xs _OpenBSD_import_etc_if_exists() {
1848 730e151b 2025-06-02 xs mkdir -p nodes/"$target"
1849 730e151b 2025-06-02 xs interfaces=$(ssh "$target" 'cd /etc && ls -1 hostname.*')
1850 730e151b 2025-06-02 xs echo >&2 "--> Importing /etc"
1851 730e151b 2025-06-02 xs for file in $etc_files_to_import $interfaces; do
1852 730e151b 2025-06-02 xs ssh "$target" test -f "'/etc/$file'" &&
1853 d8747c12 2025-06-03 xs _import_copy "$target:/etc/$file" "nodes/$target/"
1857 519b84c4 2025-06-05 xs _OpenBSD_import_etc_mail_if_exists() {
1858 8e44e3d4 2025-06-25 xs etc_mail_files_to_import='
1859 8e44e3d4 2025-06-25 xs mail/aliases
1860 8e44e3d4 2025-06-25 xs mail/smtpd.conf
1861 8e44e3d4 2025-06-25 xs mail/spamd.conf
1863 519b84c4 2025-06-05 xs mkdir -p nodes/"$target"
1864 1a4cdaf8 2025-06-05 xs echo >&2 "--> Importing /etc/mail"
1865 519b84c4 2025-06-05 xs for file in $etc_mail_files_to_import; do
1866 519b84c4 2025-06-05 xs ssh "$target" test -f "'/etc/$file'" &&
1867 519b84c4 2025-06-05 xs _import_copy "$target:/etc/$file" "nodes/$target/mail"
1871 d8747c12 2025-06-03 xs _OpenBSD_import_unbound_if_enabled() {
1872 730e151b 2025-06-02 xs ssh "$target" rcctl check unbound >/dev/null && {
1873 730e151b 2025-06-02 xs echo >&2 "--> Importing /var/unbound"
1874 730e151b 2025-06-02 xs for file in $var_unbound_files; do
1875 730e151b 2025-06-02 xs prefix=${file%%/*}
1876 730e151b 2025-06-02 xs mkdir -p "nodes/$target/unbound/$prefix"
1877 d8747c12 2025-06-03 xs _import_copy "$target:/var/unbound/$file" "nodes/$target/unbound/$prefix/"
1883 d8747c12 2025-06-03 xs _OpenBSD_import_nsd_if_enabled() {
1884 730e151b 2025-06-02 xs ssh "$target" rcctl check nsd >/dev/null && {
1885 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /var/nsd"
1886 730e151b 2025-06-02 xs for file in $var_nsd_files; do
1887 730e151b 2025-06-02 xs prefix=${file%%/*}
1888 730e151b 2025-06-02 xs mkdir -p "nodes/$target/nsd/$prefix"
1889 d8747c12 2025-06-03 xs _import_copy "$target:/var/nsd/$file" "nodes/$target/nsd/$prefix/"
1895 d8747c12 2025-06-03 xs _OpenBSD_import_postgresql_if_installed() {
1896 730e151b 2025-06-02 xs ssh "$target" postgres -V 2>/dev/null | grep -q PostgreSQL && {
1897 730e151b 2025-06-02 xs echo >&2 "--> Importing /var/postgresql"
1898 730e151b 2025-06-02 xs for file in $var_postgresql_files; do
1899 730e151b 2025-06-02 xs prefix=${file%%/*}
1900 730e151b 2025-06-02 xs mkdir -p "nodes/$target/postgresql/$prefix"
1901 d8747c12 2025-06-03 xs _import_copy "$target:/var/postgresql/$file" "nodes/$target/postgresql/$prefix/"
1903 8e44e3d4 2025-06-25 xs echo >&2 "--> Importing /etc/login.conf.d/postgresql"
1904 730e151b 2025-06-02 xs mkdir -p "nodes/$target/login.conf.d"
1905 d8747c12 2025-06-03 xs _import_copy "$target:/etc/login.conf.d/postgresql" "nodes/$target/login.conf.d/"
1913 46924c01 2025-05-28 xs _main "${@:-}"