summaryrefslogtreecommitdiff
path: root/drain
diff options
context:
space:
mode:
Diffstat (limited to 'drain')
-rwxr-xr-xdrain128
1 files changed, 128 insertions, 0 deletions
diff --git a/drain b/drain
new file mode 100755
index 0000000..c201727
--- /dev/null
+++ b/drain
@@ -0,0 +1,128 @@
+#!/usr/bin/env bash
+# Copyright 2016-2017 Luke Shumaker
+# This work is free. You can redistribute it and/or modify it under the
+# terms of the Do What The Fuck You Want To Public License, Version 2,
+# as published by Sam Hocevar. See the COPYING file for more details.
+
+# The user should not call this script directly.
+
+declare -r workdir=/var/lib/pristine-etc
+
+watchdirs=(
+ /etc
+ /usr/share/holo/files
+)
+readonly watchdirs
+
+pacman-watched-name-ver-dirs() {
+ local dir dirs
+ dirs=()
+ for dir in "${watchdirs[@]}"; do
+ if [[ -d "$dir" ]]; then
+ dirs+=("$dir")
+ fi
+ done
+ LC_ALL=C pacman -Qo "${dirs[@]}" | sed -r 's| is owned by | |' |
+ awk '{i=$2" "$3; a[i]=a[i]" "$1} END{for(i in a){print i " " a[i]}}'
+}
+
+pacman-all-name-arch() {
+ LC_ALL=C pacman -Qni | tr $'\n' $'\r' | sed 's/\r\r/\n/g' | sed -r 's|(.*\r)?Name\s*:\s*(\S+)(\r.*)?\rArchitecture\s*:\s*(\S+)\r.*|\2 \4|'
+}
+
+pacman-watched-name-arch-ver-dirs() {
+ join <(pacman-all-name-arch|sort) <(pacman-watched-name-ver-dirs|sort)
+}
+
+commit() (
+ msg="$1"
+
+ cd "$workdir"
+ if ! [[ -d etc.git ]]; then
+ mkdir -p chroot/etc
+ (cd chroot/etc && etckeeper init -d "$PWD")
+ mv chroot/etc/.git etc.git
+ fi
+ rm -rf chroot
+ mkdir chroot
+ cd chroot
+
+ err=false
+ files=()
+ while IFS=' ' read -r pkgname arch pkgver dirs; do
+ file=("/var/cache/pacman/pkg/$pkgname-$pkgver-$arch".pkg.tar.*)
+ if ! test -f "$file"; then
+ printf "ERROR: no cached package for %s %s %s\n" "$pkgname" "$pkgver" "$arch"
+ err=true
+ fi
+ files+=("$file $dirs")
+ done < <(pacman-watched-name-arch-ver-dirs)
+ if $err; then
+ return 1
+ fi
+ for filespec in "${files[@]}"; do
+ read file dirs_str <<<"$filespec"
+ read -a dirs <<<"$dirs_str"
+ printf " -> %s\n" "$file"
+ bsdtar xpvf "$file" "${dirs[@]#/}"
+ done
+
+ ln -srT ../etc.git etc/.git
+
+ if type holo &>/dev/null; then
+ mkdir -p -- run usr/lib
+ ln -sT /usr/lib/holo usr/lib/holo
+ if [ -f /etc/os-release ]; then
+ ln -sT /etc/os-release usr/lib/os-release
+ else
+ ln -sT /usr/lib/os-release usr/lib/os-release
+ fi
+ HOLO_ROOT_DIR=. holo apply
+ fi
+ cd etc/
+ etckeeper update-ignore -d "$PWD"
+ if etckeeper unclean -d "$PWD"; then
+ etckeeper commit -d "$PWD" "$msg"
+ fi
+)
+
+pull() (
+ cd /etc
+ git remote add pristine "${workdir}/chroot/etc" &>/dev/null || true
+ git fetch pristine
+)
+
+lock() {
+ local fd=$1
+ local file=$2
+ eval "exec $fd>"'"$file"'
+ flock "${@:3}" "$fd"
+}
+
+unlock() {
+ local fd=$1
+ exec {fd}>&-
+}
+
+main() {
+ set -e -o pipefail
+ umask 0022
+
+ if ! lock 7 "${workdir}/chroot.lock" -n; then
+ return 0
+ fi
+ while true; do
+ lock 8 "${workdir}/spool.lock"
+ if ! [[ -f "${workdir}/spool" ]]; then
+ return 0
+ fi
+ msg="$(cat "${workdir}/spool")"
+ rm -f "${workdir}/spool"
+ unlock 8
+
+ commit "$msg"
+ pull
+ done
+}
+
+main "$@"