#!/usr/bin/env bash # Copyright 2016-2018 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 /usr/lib/sysusers.d ) 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 etc; ln -sfT chroot/etc etc # for compatibility with old installs 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 # The holo libalpm hook runs before the systemd-sysusers # libalpm hook; but ignore that and run systemd-sysusers # first, because I said so. if type systemd-sysusers &>/dev/null; then systemd-sysusers --root=. fi if type holo &>/dev/null; then plugins=($(cat etc/holorc etc/holorc.d/* 2>/dev/null|sed -rn 's,^plugin ([a-z0-9][a-z0-9-]*)(=.*)?$,\1,p')) mkdir -p -- run usr/lib "${plugins[@]/#/usr/share/holo/}" 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 if type pwck &>/dev/null; then pwck --root "$PWD" --sort fi if type grpck &>/dev/null; then grpck --root "$PWD" --sort 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 "$@"