wip using btrfs send for sync
authorIan Kelling <ian@iankelling.org>
Thu, 10 Nov 2016 08:58:38 +0000 (00:58 -0800)
committerIan Kelling <ian@iankelling.org>
Thu, 10 Nov 2016 08:58:38 +0000 (00:58 -0800)
btrbk-run
distro-begin
distro-end
postfix-setup

index 9ca4eb3333eaffbaa8b176a71d73d240c5530d6b..4f4739d954e39d33c423ea59d06e174244893316 100755 (executable)
--- a/btrbk-run
+++ b/btrbk-run
@@ -5,32 +5,103 @@ trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
 
 [[ $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
 
@@ -54,36 +125,15 @@ target_preserve_min 6h
 # 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
@@ -94,3 +144,67 @@ if $dry_run; then
 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.
index a53cbb4f23ffac9a2d0c583f81b0df620dfe22a7..eda0b4d2f3df013810c86cef0b1145f9314d4f58 100755 (executable)
@@ -69,8 +69,10 @@ for f in iank-dev htpc treetowl x2 frodo tp li lj demohost; do
     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
@@ -319,12 +321,12 @@ case $(distro-name) in
             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
@@ -559,14 +561,16 @@ if [[ $HOSTNAME == treetowl ]]; then
     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'
@@ -580,24 +584,38 @@ if ! mountpoint /kr; then
     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
index 4a26ee12d6ac715645115d6c07a673068e6417e0..38bd60b7d2a5c10ef95f8ac272d24f4e68e9e4be 100755 (executable)
@@ -284,7 +284,8 @@ esac
 # trash can versioning for sake of space on phone, with
 # clean out after 7 days.
 #
-# did ser syncthing@ian start
+# did:
+# ser start syncthing@ian
 # then on phone, add device, hit bar code icon,
 # install bar code scanner.
 
index 9401365e6d9132d42e065fe60a48369fc14b65d3..efe27bb87aaa70d5e2d492de49ecb6eb614d9cae 100755 (executable)
 # 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
@@ -106,18 +125,76 @@ message_size_limit =  20480000
 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