summaryrefslogtreecommitdiff
path: root/drain
blob: 8f97d2d59822f6eca68460931ee89f810a1acb40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/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 "$@"