da04cb9a93a55959b9dadd02febbfcbd1715f531
[distro-setup] / conflink
1 #!/bin/bash
2 # I, Ian Kelling, follow the GNU license recommendations at
3 # https://www.gnu.org/licenses/license-recommendations.en.html. They
4 # recommend that small programs, < 300 lines, be licensed under the
5 # Apache License 2.0. This file contains or is part of one or more small
6 # programs. If a small program grows beyond 300 lines, I plan to switch
7 # its license to GPL.
8
9 # Copyright 2024 Ian Kelling
10
11 # Licensed under the Apache License, Version 2.0 (the "License");
12 # you may not use this file except in compliance with the License.
13 # You may obtain a copy of the License at
14
15 # http://www.apache.org/licenses/LICENSE-2.0
16
17 # Unless required by applicable law or agreed to in writing, software
18 # distributed under the License is distributed on an "AS IS" BASIS,
19 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 # See the License for the specific language governing permissions and
21 # limitations under the License.
22
23
24 source /a/bin/bash-bear-trap/bash-bear
25 err-cleanup() {
26 echo 1 >~/.local/conflink
27 }
28
29
30 usage() {
31 cat <<EOF
32 Usage: ${0##*/} [OPTIONS]
33 Link or otherwise install configuration files.
34
35 -f For fast. Dont use lnf, use ln -sf. Good for updating existing files.
36 -v Verbose show all the files getting linked and whatnot.
37 EOF
38 exit $1
39 }
40
41
42 s() { sudo "$@"; }
43 m() {
44 "$@"
45 }
46 v() {
47 echo "$*"
48 "$@"
49 }
50
51 lnf() { /a/exe/lnf "$@"; }
52
53
54
55 ##### begin command line parsing ########
56
57 # ensure we can handle args with spaces or empty.
58 ret=0; getopt -T || ret=$?
59 [[ $ret == 4 ]] || { echo "Install util-linux for enhanced getopt" >&2; exit 1; }
60
61 fast=false
62 verbose=false
63 temp=$(getopt -l help hvf "$@") || usage 1
64 eval set -- "$temp"
65 while true; do
66 case $1 in
67 -v) verbose=true ;;
68 -f) fast=true ;;
69 -h|--help) usage ;;
70 --) shift; break ;;
71 *) echo "$0: unexpected args: $*" >&2 ; usage 1 ;;
72 esac
73 shift
74 done
75 readonly fast verbose
76
77 ##### end command line parsing ########
78
79
80 tmpf=$(mktemp)
81 if $fast; then
82 lnf() { ln -sf "$@"; }
83 fi
84
85 if $verbose; then
86 m() {
87 echo "$*"
88 "$@"
89 }
90 fi
91
92 shopt -s nullglob
93 shopt -s extglob
94 shopt -s dotglob
95
96 # If we make a link back to the root, we stop going deeper into subdir_files.
97 # This makes it so we can do subdir directories. eg
98 # /p/c/subdir_files/.config/gajim -> ../../gagim
99 #
100 # Also note, under filesystem/, symlinks are expanded.
101
102 subdir-link-r() {
103 local root="$1"
104 local targets=()
105 if [[ $2 ]]; then
106 targets=( "$2"/!(.git|..|.|.#*) )
107 else
108 for f in "$1"/!(.git|..|.|.#*); do
109 if [[ -d $f ]]; then targets+=("$f"); fi
110 done
111 fi
112 local below
113 below="$( readlink -f "$root/..")"
114 for path in "${targets[@]}"; do
115 local fullpath
116 fullpath="$(readlink -f "$path")"
117 if [[ -f $path || $(dirname "$fullpath") == "$below" ]]; then
118 m lnf -T "$path" "$HOME/${path#"$root/"}"
119 elif [[ -d "$path" ]]; then
120 subdir-link-r "$root" "$path"
121 fi
122 done
123 }
124
125
126 common-file-setup() {
127 local dir fs x f reload_systemd
128 local -a restart_services
129 reload_systemd=false
130 # note, i ran chmod -R g-s on the filesystem dirs
131 # so i could keep permissions of secret files
132 for dir in "$@"; do
133 fs=$dir/filesystem
134 if [[ -e $fs && $user =~ ^iank?$ ]]; then
135 # we dont want t, instead c for checksum.
136 # That way we dont set times on directories.
137 # -a = -rlptgoD
138 # -A is acls, implies -p
139 cmd=( s rsync -rclgoDiSAX --chmod=Dg-s --chown=root:root
140 --exclude=/etc/dovecot/users
141 --exclude='/etc/exim4/passwd*'
142 --exclude='/etc/exim4/*.pem'
143 $fs/ / )
144 echo "${cmd[@]@Q}"
145 "${cmd[@]}" | tee $tmpf
146 while read -r line; do
147 file="${line:12}"
148 case $file in
149 etc/prometheus/rules/iank.yml|etc/prometheus/prometheus.yml)
150 case $HOSTNAME in
151 kd)
152 if systemctl is-active prometheus &>/dev/null; then
153 v s systemctl reload prometheus
154 fi
155 ;;
156 esac
157 ;;
158 etc/systemd/system/*)
159 reload_systemd=true
160 ;;
161 etc/dnsmasq.d/*)
162 restart_services+=(dnsmasq)
163 ;;
164 etc/systemd/resolved.conf.d/*)
165 restart_services+=(systemd-resolved)
166 ;;
167 esac
168 # Previously did this with tar, but it doesn't
169 # update directory permissions.
170 #
171 # S = do spare files efficiently
172 # A = preserve acls
173 # X = preserve extended attributes
174 # i = itemize
175 done <$tmpf
176 fi
177
178 if ! $fast && [[ -e $dir/subdir_files ]]; then
179 m subdir-link-r $dir/subdir_files
180 fi
181 local x=( $dir/!(binds|subdir_files|filesystem|machine_specific|..|.|.#*) )
182 (( ${#x[@]} >= 1 )) || continue
183 m lnf ${x[@]} ~
184 done
185 if $reload_systemd; then
186 v s systemctl daemon-reload
187 fi
188 for service in ${restart_services[@]}; do
189 if systemctl is-active $service >/dev/null; then
190 v s systemctl restart $service
191 fi
192 done
193 }
194
195 user=$(id -un)
196 all_dirs=({/a/bin/ds,/p/c}{,/machine_specific/$HOSTNAME})
197 # note, we assume a group of hosts does not have the
198 # same name as a single host, which is no problem on our scale.
199 for x in /p/c/machine_specific/*.hosts /a/bin/ds/machine_specific/*.hosts; do
200 if grep -qxF $HOSTNAME $x; then all_dirs+=( ${x%.hosts} ); fi
201 done
202
203
204 c_dirs=(/a/c{,/machine_specific/$HOSTNAME})
205 case $user in
206 iank)
207 # old files 2022-03
208 for t in systemstatus epanicclean btrfsmaintstop dynamicipupdate; do
209 f=/etc/systemd/system/$t.timer
210 if [[ -e $f ]]; then
211 v systemctl stop $t.timer
212 v systemctl disable $t.timer
213 s rm -fv $f
214 reload_systemd=true
215 fi
216 done
217 # old 2022-04
218 if [[ -e /etc/cron.daily/check-lets-encrypt-ssl-settings ]]; then
219 m s rm -f /etc/cron.daily/check-lets-encrypt-ssl-settings
220 fi
221 # conversion from whole folder subdir to individual files.
222 if [[ -L /home/iank/.config/copyq ]]; then
223 rm -fv /home/iank/.config/copyq
224 fi
225
226 /a/bin/ds/install-my-scripts
227 files=(/p/c/machine_specific/*/filesystem/etc/ssh/*_key
228 /p/c/machine_specific/*/filesystem/etc/openvpn/client/*.key
229 /p/c/filesystem/etc/openvpn/client/*.key
230 /p/c/filesystem/etc/openvpn/easy-rsa/keys/*.key
231 )
232 if [[ -e ${files[0]} ]]; then
233 chmod 600 ${files[@]}
234 fi
235 # p needs to go first so .ssh link is created, then config link inside it
236 m common-file-setup ${all_dirs[@]}
237
238 #### begin special extra stuff ####
239 install -d -m700 ~/gpg-agent-socket
240
241 f=/var/lib/bind
242 if [[ -e $f ]]; then
243 # reset to the original permissions.
244 m s chgrp -R bind $f
245 m s chmod g+w $f
246 fi
247 # shellcheck disable=SC2016 # obviously expected
248 s bash -c 'shopt -s nullglob; for f in /etc/bind/*.key /etc/bind/*.private /etc/bind/key.*; do chgrp bind $f; done'
249 if [[ -e /etc/caldav-htpasswd ]] && getent group www-data &>/dev/null; then
250 s chgrp www-data /etc/caldav-htpasswd
251 fi
252 if [[ -e /var/lib/znc ]] && getent group znc; then
253 s chown -R znc:znc /var/lib/znc
254 fi
255 if [[ -e /p/c/user-specific ]]; then
256 if getent passwd prometheus &>/dev/null; then
257 v s rsync -clpgoDiSAX --chmod=Dg-s --chown=root:prometheus /p/c/user-specific/prometheus/prometheus-pass /etc
258 v s rsync -clpgoDiSAX --chmod=Dg-s --chown=root:prometheus /p/c/user-specific/prometheus/prometheus/ssl/* /etc/prometheus/ssl
259 fi
260 if getent passwd www-data &>/dev/null; then
261 v s rsync -clpgoDiSAX --chmod=Dg-s --chown=root:www-data /p/c/user-specific/www-data/* /etc
262 fi
263 fi
264
265 if [[ -d /var/lib/bitcoind && -d /p/c/user-specific/bitcoin ]]; then
266 s rsync -clpgoDiSAX --chmod=Dg-s --chown=bitcoin:bitcoin /p/c/user-specific/bitcoin/settings.json /var/lib/bitcoind
267 s rsync -rclpgoDiSAX --chmod=Dg-s --chown=root:bitcoin /p/c/user-specific/bitcoin/bitcoin /etc
268 fi
269 # this folder strangely requires ownership as icecast2
270 if [[ -d /etc/icecast2 && -f /p/c/icecast.xml ]]; then
271 m s rsync -rclgoDiSAX --chmod=0644 --chown=root:root /p/c/icecast.xml /etc/icecast2
272 fi
273 ##### end special extra stuff #####
274
275 if ! $fast; then
276 m s -H -u user2 "${BASH_SOURCE[0]}"
277 fi
278
279 f=/a/bin/distro-setup/system-status
280 if [[ -x $f ]]; then
281 $f _
282 fi
283 mkdir -p ~/.local
284 echo 0 >~/.local/conflink
285
286 ;;
287 user2)
288 m common-file-setup ${c_dirs[@]}
289 ;;
290 *)
291 echo "$0: error: unexpected user"; exit 1
292 ;;
293 esac