X-Git-Url: https://iankelling.org/git/?p=distro-setup;a=blobdiff_plain;f=check-subvol-stale;h=8e7d7547457a4e02f01efeaf4720f4a9bf9a699a;hp=f012c4f97459f38d0e24ff75df3f13c7fca68dfb;hb=HEAD;hpb=ce4cacd36c5b5babeea85d0f93771017e6169180 diff --git a/check-subvol-stale b/check-subvol-stale index f012c4f..9b5e88d 100644 --- a/check-subvol-stale +++ b/check-subvol-stale @@ -1,12 +1,19 @@ #!/bin/bash -# Copyright (C) 2016 Ian Kelling -# +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to switch +# its license to GPL. + +# Copyright 2024 Ian Kelling + # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# + # http://www.apache.org/licenses/LICENSE-2.0 -# + # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,9 +21,10 @@ # limitations under the License. + [[ $EUID == 0 ]] || exec sudo -E "${BASH_SOURCE[0]}" "$@" -source /usr/local/lib/err +set -e; . /usr/local/lib/bash-bear; set +e shopt -s nullglob @@ -77,9 +85,11 @@ stale-file() { fi } +pre="check-subvol-stale:${SSH_CLIENT:+ $HOSTNAME:}" + d() { if $verbose; then - printf "%s\n" "$*" + printf "$pre %s\n" "$*" fi } @@ -97,8 +107,10 @@ mapper-dev() { done fi } - +tmpf=$(mktemp) +d tmpf=$tmpf for d; do + if $subvol_path; then svp=$d root_dir=${d%/*} @@ -142,14 +154,55 @@ for d; do d "svp=$svp # subvolume path" fi - snaps=($root_dir/btrbk/$subvol_dir.20*) # Assumes we are in the 21st century. - if [[ ! ${snaps[*]} ]]; then + # note: relying on null glob + ls_args=($root_dir/btrbk/$subvol_dir.20*) + if (( ${#ls_args[@]} )); then + # Assumes we are in the 21st century. + ls -1dvrq $root_dir/btrbk/$subvol_dir.20* >$tmpf + mapfile -t snaps <$tmpf + else # no snapshots yet - # TODO: make this an error and override with a cli flag + # TODO: consider making this an error and override with a cli flag echo "$0: warning: no snapshots found at $root_dir/btrbk/$subvol_dir.20*. this is expected for a brand new volume" continue fi + # last_snap by date. + last_snap="${snaps[0]}" + + case $last_snap in + $root_dir/btrbk/$subvol_dir.20*) : ;; + *) + echo "$0: error: unexpected last_snap:$last_snap" + exit 1 + ;; + esac + + d last_snap=$last_snap + ## alternate slower alternative which would not rely on ls sorting: + # last_snap=$( + # for s in ${snaps[@]}; do + # f=${s##*/} + # unix_time=$(date -d $(sed -r 's/(.{4})(..)(.{5})(..)(.*)/\1-\2-\3:\4:\5/' <<<${f#$vol.}) +%s) + # printf "%s %s\n" $unix_time $s # part of the pipeline + # # sort will fail + # done | sort -r | head -n 1 | awk '{print $2}' || [[ ${PIPESTATUS[1]} == 141 || ${PIPESTATUS[0]} == 32 ]] + # ) + # if [[ ! $last_snap ]]; then + # # should not happen. + # echo "$0: error: could not find latest snapshot for $svp among ${snaps[*]}" >&2 + # exit 1 + # fi + + if [[ ! -e $svp ]]; then + echo "$0: warning: subvol does not exist: $svp" + echo "$0 assuming this host was just for receiving and latest snap is freshest" + freshest_snap=$last_snap + stale=true + stale-file + continue + fi + # get info on last received sub last_received= last_received_cgen=0 @@ -161,39 +214,17 @@ for d; do if [[ $cgen -gt $last_received_cgen ]]; then last_received_cgen=$cgen last_received=$f + elif [[ $last_received ]]; then + # optimization: we are looking in reverse order by date, so if + # we find one that has a lesser cgen, assume the rest will all + # be lesser. + break fi fi done d last_received_cgen=$last_received_cgen d last_received=$last_received - # Get last_snap by date. - # when a btrbk bugfix makes it into the distro, - # we might replace this with btrbk list latest /mnt/root/$vol | ... - last_snap=$( - for s in ${snaps[@]}; do - f=${s##*/} - unix_time=$(date -d $(sed -r 's/(.{4})(..)(.{5})(..)(.*)/\1-\2-\3:\4:\5/' <<<${f#$vol.}) +%s) - printf "%s %s\n" $unix_time $s # part of the pipeline - # sort will fail - done | sort -r | head -n 1 | awk '{print $2}' || [[ ${PIPESTATUS[1]} == 141 || ${PIPESTATUS[0]} == 32 ]] - ) - if [[ ! $last_snap ]]; then - # should not happen. - echo "$0: error: could not find latest snapshot for $svp among ${snaps[*]}" >&2 - exit 1 - fi - d last_snap=$last_snap - - if [[ ! -e $svp ]]; then - echo "$0: warning: subvol does not exist: $svp" - echo "$0 assuming this host was just for receiving and latest snap is freshest" - freshest_snap=$last_snap - stale=true - stale-file - continue - fi - # if there is a last_received, we can assume stale or fresh if we are newer/older if [[ $last_received ]]; then @@ -215,7 +246,7 @@ for d; do stale=true # fresh if $svp has $last_snap as a snapshot, if btrfs sub show $svp 2>/dev/null | sed '0,/^\s*Snapshot(s):/d;s/^\s*//' | \ - grep -xF ${last_snap#$root_dir/} ; then + grep -xF ${last_snap#"$root_dir"/} >/dev/null; then stale=false else # or else $svp is a snapshot of $last_snap. we use a uuid # comparison, which if I remember from the docs, is a bit more @@ -228,3 +259,4 @@ for d; do stale-file done +rm $tmpf