chiark / gitweb /
hexterm
[chiark-utils.git] / backup / snaprsync
1 #!/bin/bash
2 #
3 # usage: snaprsync <setting>... <positionals>
4 #  <setting> is --<name>=<value>
5 #  <positionals> are assigned to unused mandatory values in order
6 # mandatory:
7 #   rhost device mountpoint localarea 
8 # optional:
9         localprevious=
10         snapkind=lvm
11         rsharedir=/usr/share/chiark-backup 
12         retcdir=/etc/chiark-backup
13         rvardir=/var/lib/chiark-backup
14         bwlimit=
15         subdir=.
16         rsyncopts=
17         summer=summer
18
19 set -e
20
21 badusage () { echo >&2 "snaprsync: bad usage: $1"; exit 12; }
22 nb_echo () { (echo "$@"); } # See Debian #382798
23 x () { nb_echo "+ $@"; "$@"; }
24 xspawned () { eval "${1}pid=$!; nb_echo \"+[$!] ($1) &\";"; }
25 xwait () { eval "nb_echo \"+[\$${1}pid] ($1)...\"; wait \$${1}pid;"; }
26
27 while true; do
28         case "$1" in
29         --?*=*)
30                 name=${1#--}; name=${name%%=*}
31                 value=${1#--*=}
32                 case "$name" in
33                 rhost|device|mountpoint|localarea);;
34                 localprevious|snapkind|rsharedir|retcdir|rvardir|bwlimit);;
35                 subdir|rsyncopts|summer);;
36                 *) badusage "unknown setting $name";;
37                 esac
38                 eval "$name=\$value"
39                 ;;
40         --)     shift; break ;;
41         -*)     badusage "unknown option $1" ;;
42         *)      break ;;
43         esac
44         shift
45 done
46
47 for name in rhost device mountpoint localarea; do
48         eval "value=\$$name"
49         if [ "x$value" != x ]; then continue; fi
50         if [ $# = 0 ]; then badusage "no value for setting $name"; fi
51         eval "$name=$1"
52         shift
53 done
54
55 datefmt='%Y-%m-%d %H:%M:%S Z'
56 rsync="rsync ${bwlimit:+--bwlimit} $bwlimit"
57 export RSYNC_RSH='ssh -o compression=no'
58 sshpfx='PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin; export PATH; '
59
60 ssh $rhost "$sshpfx date -u '+$rhost $datefmt start'"
61 ssh $rhost "$sshpfx id"
62 ssh $rhost "$sshpfx ls -d $rsharedir"
63 ssh $rhost "$sshpfx ls -d $rvardir"
64
65 test -d $localarea || x mkdir $localarea
66 ournode=`uname -n`
67 rsumsfile=for-$ournode.sums
68 summer="$summer -ACDbtqf"
69
70 td=/dev/enoent
71 rc=12
72 trap 'rm -rf $td; exit $rc' 0
73 td=`mktemp -td`
74
75 mkfifo -m 600 $td/sentinel
76 exec 4<>$td/sentinel
77
78 x ssh $rhost "$sshpfx $rsharedir/snap-drop"
79 ssh $rhost "
80         $sshpfx
81         set -e
82         cd $rvardir
83         echo '$retcdir/snap/$snapkind drop $rvardir' >snap-drop.new
84         mv snap-drop.new snap-drop
85 "
86 x ssh $rhost "$sshpfx $retcdir/snap/$snapkind snap $rvardir $device $mountpoint"
87 ssh $rhost <$td/sentinel 4<&- "
88   $sshpfx
89   set -e
90   date -u '+$rhost $datefmt main'
91   exec 3<&0 0</dev/null
92   (set +e; read x <&3; kill 0) &
93   cd $rvardir
94   umask 077
95   exec 3>$rsumsfile
96   cd snap-mount
97   $summer . >&3
98   date -u '+$rhost $datefmt sumsdone'
99   cd ..
100 " &
101 xspawned rsum
102 x $rsync -aHSxz --numeric-ids --delete $rsyncopts \
103         ${localprevious:+--link-dest} $localprevious \
104         $rhost:$rvardir/snap-mount/$subdir $localarea/.
105 date -u "+ $datefmt rsyncdone"
106
107 exec 3>$localarea,lsums
108 (cd $localarea && \
109  $summer . >&3) &
110 xspawned lsum
111 exec 3>&-
112
113 xwait rsum
114 exec 4<&-
115 date -u "+ $datefmt sumsdone"
116 x ssh $rhost "$sshpfx $rsharedir/snap-drop"
117
118 if [ "x${localprevious}" != x ] && test -f "$localprevious,rsums"; then
119         cp "$localprevious,rsums" "$localarea,rsums"
120 fi
121 x $rsync -p \
122         $rhost:$rvardir/$rsumsfile \
123         "$localarea,rsums"
124
125 xwait $lsum
126 date -u "+ $datefmt checking"
127
128 set +e
129 diff -u "$localarea,rsums" "$localarea,lsums" >"$localarea,sumsdiff"
130 diffrc=$?
131 set -e
132 test $diffrc = 0 || test $diffrc = 1
133
134 date -u "+ $datefmt checked $diffrc"
135 rc=$diffrc