[[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@"
+usage() {
+ echo "top of script file:"
+ sed -n '1,/^[# ]*end command line/{p;b};q' "$0"
+ exit $1
+}
+
conf_only=false
-dry_run=false
-# mostly for testing
-case $1 in
- -c) conf_only=true ;;
- -n) dry_run=true ;;
- ?*) echo "$0: error: unsupported arg"; exit 1 ;;
-esac
-
-# background on timezones. with short/long, timestamps use local time.
-# for long, if your local time moves backwards, by moving timezones or
-# for an hour when daylight savings changes it, you will temporarily get
-# a more aggressive retention policy for the overlapping period, and
-# vice versa for the opposite timezone move. The alternative is using
-# long-iso, which puts timezone info into the timestamp, which means
-# that instead of shifting time, you shift the start of day/week/month
-# which is used for retention to your new local time, which means for
-# example, if you moved forward by 8 hours, the daily/weekly/monthly
-# retention will be 8 hours more aggressive since midnight is at a new
-# time, unless you fake the timzeone using the TZ env variable.
-# However, in the short term, there will be no inconsistencies.
-# I don't see any problem with shifting when the day starts for
-# retention, so I'm using long-iso.
+dry_run=false # mostly for testing
+
+temp=$(getopt -l help,long-opt hcnt "$@") || usage 1
+eval set -- "$temp"
+while true; do
+ case $1 in
+ -c) conf_only=true; shift ;;
+ -n) dry_run=true; dry_run_arg=-n; shift ;;
+ -t) IFS=, targets=($2); shift 2 ;;
+ -h|--help) usage ;;
+ --) shift; break ;;
+ *) echo "$0: Internal error!" ; exit 1 ;;
+ esac
+done
+read primary <<<"$@"
+
+##### end command line parsing ########
+
+sed="sed -r --follow-symlinks"
+last_snaps=()
+
+target-section() {
+ local root=$1
+ local subvol=$2
+ mountpoint $root &>/dev/null || return
+ cat >>/etc/btrbk.conf <<EOF
+volume $root
+subvolume $subvol
+$remote_target
+
+EOF
+}
+
+rsync-dirs() {
+ local host=$1
+ local path=$2
+ rsync $dry_run_arg -ahi --relative --delete "$path" "root@$host:/"
+}
-target_host=frodo
-cat >/etc/btrbk.conf <<'EOF'
+last-snap() {
+ vol=${1##*/}
+ cd /mnt/root
+ last_snap=$(
+ for f in $vol.20*; do
+ printf "%s %s\n" $(date -d $(sed -r 's/(.{4})(..)(.{5})(..)(.*)/\1-\2-\3:\4:\5/' <<<${f#$vol.}) +%s) $f
+ done | sort -r | head -n 1 | awk '{print $2}'
+ )
+ last_snaps+=($last_snap)
+}
+
+# note q is owned by root:1000
+# note p is owned 1000:1000 and chmod 700
+mountpoints=(/q)
+if mountpoint /p; then
+ mountpoints+=(/p)
+fi
+
+if [[ ! $targets ]]; then
+ case $HOSTNAME in
+ tp|x2)
+ if ! timeout -s 9 10 ssh frodo :; then
+ targets=($HOME_DOMAIN)
+ fi
+ ;;
+ esac
+ targets=(frodo)
+fi
+
+
+# umount first to ensure we don't have any errors
+# todo: do some kill fuser stuff to make umount more reliable
+# todo: setup sync systemd timer on $primary, once per hour.
+# todo: setup lock so that if this is already running, we exit out, so
+# that manual runs don't interfere with cronjobs.
+if [[ $primary ]] && ! $dry_run; then
+ for m in ${mountpoints[@]}; do
+ # note, this won't work for /i, due to path being /mnt/iroot
+ # todo: include /i for treetowl/frodo
+ btrfs property set -ts /mnt/root$m ro true
+ ssh root@$primary bash <<EOF
+set -ex
+umount $m
+[[ -e /mnt/root$m ]] || exit 0
+btrfs sub del /mnt/root$m
+EOF
+ done
+fi
+
+for tg in ${targets[@]}; do
+ cat >/etc/btrbk.conf <<'EOF'
ssh_identity /root/.ssh/id_rsa
transaction_syslog daemon
# btrbk -l debug -v dryrun
EOF
+ remote_target="target send-receive ssh://${tg}/mnt/root"
-case $HOSTNAME in
- tp|x2)
- if ! timeout -s 9 10 ssh frodo :; then
- target_host=$HOME_DOMAIN
- cat >>/etc/btrbk.conf <<EOF
-ssh_port 2222
-EOF
- fi
- ;;
-esac
-
-if [[ $HOSTNAME != frodo ]]; then
- remote_target="target send-receive ssh://${target_host}/mnt/root/${HOSTNAME}-btrbk"
-fi
-
-target-section() {
- root=$1
- subvol=$2
- mountpoint $root &>/dev/null || return
- cat >>/etc/btrbk.conf <<EOF
-volume $root
-subvolume $subvol
-$remote_target
-
-EOF
-}
-
-target-section /mnt/iroot i
-target-section /mnt/root q
+ if [[ $tg == frodo && $HOSTNAME == treetowl ]]; then
+ target-section /mnt/iroot i
+ fi
+ for m in ${mountpoints[@]}; do
+ target-section /mnt/root ${m##*/}
+ done
+done
if $conf_only; then
exit
else
btrbk -q run
fi
+
+# if we have /p, rsync to targets without /p
+if mountpoint /p; then
+ for tg in ${targets[@]}; do
+ case $tg in
+ tp|li|lk)
+ # todo, test this
+ for x in /p/c/machine_specific/*.hosts; do
+ if grep -qxF $tg $x; then
+ dir=${x%.hosts}
+ rsync-dirs ${dir##*/} $dir
+ fi
+ done
+ ;;
+ esac
+ done
+fi
+
+first_root=$(awk '$2 == "/mnt/root" {print $1}' /etc/mtab)
+
+# make $primary have the rw snapshot
+if [[ $primary ]] && ! $dry_run; then
+ fstab=()
+ for m in ${mountpoints[@]}; do
+ last-snap $m
+ fstab+=("$first_root $m btrfs noatime,subvol=$last_snap 0 0")
+ done
+
+ printf "%s\n" "${fstab[@]}" | cedit /etc/fstab
+ for d in ${mountpoints[@]}; do
+ mount $d
+ btrfs sub del /mnt/root$d
+ done
+ ssh root@primary bash -s "${mountpoints[*]}" "${last_snaps[*]}" <<'EOF'
+set -xe
+mountpoints=($1)
+last_snaps=($2)
+first_root=$(awk '$2 == "/mnt/root" {print $1}' /etc/mtab)
+for ((i=0; i < ${#mountpoints[@]}; i++)); do
+ m=${mountpoints[i]}
+ vol=${m##*/}
+ fstab+=("$first_root $m btrfs noatime,subvol=$vol 0 0")
+ cd /mnt/root
+ btrfs sub snapshot ${last_snaps[i]} $vol
+ mount $m
+done
+EOF
+fi
+
+
+# background on btrbk timezones. with short/long, timestamps use local time.
+# for long, if your local time moves backwards, by moving timezones or
+# for an hour when daylight savings changes it, you will temporarily get
+# a more aggressive retention policy for the overlapping period, and
+# vice versa for the opposite timezone move. The alternative is using
+# long-iso, which puts timezone info into the timestamp, which means
+# that instead of shifting time, you shift the start of day/week/month
+# which is used for retention to your new local time, which means for
+# example, if you moved forward by 8 hours, the daily/weekly/monthly
+# retention will be 8 hours more aggressive since midnight is at a new
+# time, unless you fake the timzeone using the TZ env variable.
+# However, in the short term, there will be no inconsistencies.
+# I don't see any problem with shifting when the day starts for
+# retention, so I'm using long-iso.
eval "$f() { [[ $HOSTNAME == $f ]]; }"
done
has_p() { treetowl || iank-dev || x2 || frodo || tp || demohost; }
-has_x() { ! { lj || li; }; }
+has_x() { ! linode; }
linode() { lj || li; }
+has_btrfs() { ! linode; }
+home_network() { ! linode; }
encrypted() { has_p; }
shopt -s extglob
if isdebian-stable; then
pi firefox/$codename-backports
else
- # for a while, firefox/unstable had all it\'s deps satisfied
- # by testing packages, but now i hit a conflict,
- # it wanted a newer libfontconfig1, but emacs build-deps
- # wanted an older one. Oh well, they seem to release
- # a new esr version every 9 months or so.
- pi firefox-esr
+ # for a while, firefox/unstable did not have
+ # dependencies satisfied by testing packages, and i hit
+ # a conflict, it wanted a newer libfontconfig1, but
+ # emacs build-deps wanted an older one. note: They seem
+ # to release a new esr version every 9 months or so.
+ pi firefox/unstable
fi
fi
# for hosts which require nonfree drivers
tu /etc/fstab <<EOF
/dev/mapper/crypt_dev_$dev /i btrfs noatime,subvol=i 0 0
EOF
+
+ tu /etc/crypttab <<EOF
+crypt_dev_$dev /dev/disk/by-id/$dev /q/root/luks/host-treetowl discard,luks
+EOF
+
else
tu /etc/fstab <<'EOF'
/q/i /i none bind 0 0
EOF
- tu /etc/crypttab <<EOF
-crypt_dev_$dev /dev/disk/by-id/$dev /q/root/luks/host-treetowl discard,luks
-EOF
fi
tu /etc/fstab <<'EOF'
s mkdir -p /kr
s chown ian:traci /kr
fi
-if [[ $HOSTNAME == treetowl ]]; then
- tu /etc/fstab <<'EOF'
+
+if home_network; then
+ if [[ $HOSTNAME == treetowl ]]; then
+ tu /etc/fstab <<'EOF'
/k /kr none bind 0 0
EOF
-else
- tu /etc/fstab <<'EOF'
+ else
+ tu /etc/fstab <<'EOF'
treetowl:/k /kr nfs defaults 0 0
EOF
+ fi
fi
s mkdir -p /q/i/{w,k}
for dir in /{i,w,k}; do
- if mountpoint $dir; then continue; fi
+ if mountpoint $dir; then continue; fi # already mounted
s mkdir -p $dir
s chown ian:ian $dir
s mount $dir
done
+dir=/nocow
+l=$(sed -rn "s#^(\s*\S+\s+)/q(\s.*)(subvol=q)#\1$dir\2subvol=nocow#p" /etc/fstab)
+if [[ $l ]] && ! mountpoint $dir; then
+ tu /etc/fstab <<EOF
+$l
+EOF
+ s mkdir -p $dir
+ s chown ian:ian $dir
+ s mount $dir
+fi
+
# ssh and probably some other things care about parent directory
# ownership, and ssh doesn\'t allow any group writable parent
# See the License for the specific language governing permissions and
# limitations under the License.
-set -eE -o pipefail
-trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
-# dunno why debian installed postfix with builddep emacs
-# but I will just explicitly install it here since
-# I use it for sending mail in emacs.
-if private-host; then
- relayhost="[mail.messagingengine.com]:587"
-else
- # ses initially suggests port 25, but I had problems connecting to that.
- relayhost="[email-smtp.us-west-2.amazonaws.com]:587"
-fi
-if isdeb; then
- s debconf-set-selections <<EOF
-postfix postfix/main_mailer_type select Satellite system
-postfix postfix/mailname string $HOSTNAME
-postfix postfix/relayhost string $relayhost
-EOF
+# misc exim notes:
+# useful exim docs:
+# /usr/share/doc/exim4-base/README.Debian.gz
+# /usr/share/doc/exim4-base/spec.txt.gz
- pi postfix
-else
- pi postfix
- # Settings from reading the output when installing on debian,
- # then seeing which were different in a default install on arch.
- # I assume the same works for fedora.
- postconfin <<EOF
-mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
-mailbox_size_limit = 0
-relayhost = $relayhost
-inet_interfaces = loopback-only
-EOF
+# routers, transports, and authenticators are sections, and you define
+# driver instances in those sections, and the manual calls them driver
+# types but there is also a more specific "type" of driver, which is specified
+# with the driver = some_module setting in the driver.
- s systemctl enable postfix
- s systemctl start postfix
-fi
+# the driver option must precede and private options (options that are
+# specific to that driver), so follow example of putting it at beginning.
+# The full list of option settings for any particular driver instance,
+# including all the defaulted values, can be extracted by making use of
+# the -bP command line option.
-# note, previously, the rest of setup was done separately.
+# exim clear out message queue. as root:
+# adapted from somewhere on stackoverflow.
+# ser stop exim4; sleep 1; exim -bp | exiqgrep -i | xargs exim -Mrm; ser start exim4
+# fastmail has changed their smtp server, but the old one still works,
+# I see no reason to bother changing.
+# New one is smtp.fastmail.com
-# based on,http://www.postfix.org/qmgr.8.html and my notes in gnus
-# originally tried moving specific directories under /var/spool/postfix,
-# but postfix didn't like that
-if [[ ! -L /var/spool/postfix ]]; then
- ser stop postfix
- n=/q/postfix-`distro-name``debian-archive`
- if [[ -e $n ]]; then
- echo "$0: warning: $n already exists before we do the link, removing it"
- s rm -rf $n
- fi
- s mv /var/spool/postfix $n
- s lnf -T $n /var/spool/postfix
- ser start postfix
- s journalctl -n 20 | cat # sudo as we may not have journal reading rights yet
+# test delivery & rewrite settings:
+#exim4 -bt ian@localhost
+
+
+set -eE -o pipefail
+trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
+
+type=$1
+postfix() { [[ $type == postfix ]]; }
+exim() { [[ $type == exim ]]; }
+if ! exim && ! postfix; then
+ echo "$1: error: expected exim or postfix as first arg"
+ exit 1
fi
+if private-host; then
+ host=mail.messagingengine.com
+ forward=$HOSTNAME@$PERSONAL_DOMAIN
+else
+ # ses initially suggests port 25, but I had problems connecting to that.
+ host=email-smtp.us-west-2.amazonaws.com
+ forward=$HOSTNAME@$IMPERSONAL_DOMAIN
+fi
-# This also works instead of ~/.forward
+relayhost="[$host]:587" # postfix
+smarthost="$host::587" # exim
+
+# background: This also works instead of ~/.forward
# s sed -i --follow-symlinks '/^root/d' /etc/aliases ||:
#echo "root: $HOSTNAME@$SOME_DOMAIN" | s tee -a /etc/aliases
# this can't be a symlink and has permission restrictions
# it might work in /etc/aliases, but this seems more proper.
-
-if s grep amazonaws /etc/postfix/sasl_passwd &>/dev/null; then
- forward=$HOSTNAME@$IMPERSONAL_DOMAIN
-else
- forward=$HOSTNAME@$PERSONAL_DOMAIN
-fi
e $forward > ~/.forward
e $forward | s tee /root/.forward
# linode image has a root alias. completely useless, remove it.
sudo sed -i '/^root:/d' /etc/aliases
-
s newaliases
-# if I wanted the from address to be renamed and sent to a different address,
-# echo "sdx@localhost development@localhost" | sudo dd of=/etc/postfix/recipient_canonical
-# sudo postmap hash:/etc/postfix/recipient_canonical
-# sudo service postfix reload
+# offlineimap uses this too, it is much easier to use one location than to
+# condition it's config and postfix's config
+case $distro in
+ fedora) s lnf -T ca-certificates.crt /etc/ssl/ca-bundle.trust.crt ;;
+ *) :
+esac
+
+read -r domain pass < <(s cat /etc/mailpass)
+if postfix; then
+ # dunno why, but debian installed postfix with builddep emacs
+ # but I will just explicitly install it here since
+ # I use it for sending mail in emacs.
+ if isdeb; then
+ s debconf-set-selections <<EOF
+postfix postfix/main_mailer_type select Satellite system
+postfix postfix/mailname string $HOSTNAME
+postfix postfix/relayhost string $relayhost
+EOF
+
+ pi postfix
+ else
+ pi postfix
+ # Settings from reading the output when installing on debian,
+ # then seeing which were different in a default install on arch.
+ # I assume the same works for fedora.
+ postconfin <<EOF
+mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
+mailbox_size_limit = 0
+relayhost = $relayhost
+inet_interfaces = loopback-only
+EOF
-# i'm assuming mail just won't work on systems without the sasl_passwd.
-postconfin <<'EOF'
+ s systemctl enable postfix
+ s systemctl start postfix
+ fi
+ # i'm assuming mail just won't work on systems without the sasl_passwd.
+ postconfin <<'EOF'
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
inet_protocols = ipv4
EOF
-# msg_size_limit: I ran into a log file not sending cuz of size. double from 10 to 20 meg limit
-# inet_protocols: without this, postfix tries an ipv6 lookup then gives
-# up and fails. snippet from syslog: type=AAAA: Host not found, try again
+ # msg_size_limit: I ran into a log file not sending cuz of size. double from 10 to 20 meg limit
+ # inet_protocols: without this, postfix tries an ipv6 lookup then gives
+ # up and fails. snippet from syslog: type=AAAA: Host not found, try again
+
+
+ # mailpass is just a name i made up, since postfix and
+ # exim both use a slightly crazy format to translate to
+ # each other, it's easier to use my own format.
+ f=/etc/postfix/sasl_passwd
+ s touch $f
+ s chmod 600 $f
+ echo "[$domain]:587 ${pass/@/#}" | s dd of=/etc/postfix/sasl_passwd >/dev/null
+ s postmap hash:/etc/postfix/sasl_passwd
+ s service postfix reload
+else
+ # wording of question from dpkg-reconfigure exim4-config
+ # 1. internet site; mail is sent and received directly using SMTP
+ # 2. mail sent by smarthost; received via SMTP or fetchmail
+ # 3. mail sent by smarthost; no local mail
+ # 4. local delivery only; not on a network
+ # 5. no configuration at this time
+
+ # default mailname is $HOSTNAME.lan,
+ # mailname makes addresses like "root" be root@mailname
+ # and a qualified domain does not get forwarded per
+ # .forward. whatever, this fixes that.
+ s debconf-set-selections <<EOF
+exim4-config exim4/dc_eximconfig_configtype select mail sent by smarthost; no local mail
+exim4-config exim4/dc_smarthost string $smarthost
+exim4-config exim4/use_split_config boolean true
+exim4-config exim4/mailname string $HOSTNAME
+EOF
+ # light version does not have sasl auth support.
+ pi exim4-daemon-heavy
+
+ f=/etc/exim4/passwd.client
+ s touch $f
+ s chmod 600 $f # make it 600 before writing sensitive info
+ echo "$domain:${pass/:/::}" | s dd of=$f >/dev/null
+ # https://blog.dhampir.no/content/make-exim4-on-debian-respect-forward-and-etcaliases-when-using-a-smarthost
+ # i only need .forwards, so just doing that one.
+ cd /etc/exim4/conf.d/router
+ a=userforward
+ b=${a}_higher_priority
+ tmp=$(mktemp)
+ of=175_$b
+ # sed to make the router name unique
+ sed -r s/^\\S+:/$b:/ 600_exim4-config_$a >$tmp
+ if diff -q >/dev/null $tmp $of; then
+ s dd if=$tmp of=$of >/dev/null
+ ser restart exim4
+ fi
+fi
+
+# based on http://www.postfix.org/qmgr.8.html and my notes in gnus
+dir=/nocow/$type
+sdir=/var/spool/$type
+if [[ $(readlink -f $sdir) != $dir ]]; then
+ ser stop $type
+ if [[ ! -e $dir && -d $sdir ]]; then
+ s mv $sdir $dir
+ fi
+ s lnf -T $dir $sdir
+fi
+
+sgo $type
-s postmap hash:/etc/postfix/sasl_passwd
-# offlineimap uses this too, it is much easier to use one location than to
-# condition it's config and postfix's config
-case $distro in
- fedora) s lnf -T ca-certificates.crt /etc/ssl/ca-bundle.trust.crt ;;
- *) :
-esac
-s service postfix reload
-sgo postfix
+# if I wanted the from address to be renamed and sent to a different address,
+# echo "sdx@localhost development@localhost" | sudo dd of=/etc/postfix/recipient_canonical
+# sudo postmap hash:/etc/postfix/recipient_canonical
+# sudo service postfix reload