@@ -20,12 +20,43 @@ DIFFOSCOPE="diffoscope"
2020orig_argv=(" $0 " " $@ " )
2121src_owner=${SUDO_USER:- $USER }
2222function check_root() {
23- (( EUID == 0 )) && return
24- if type -P sudo > /dev/null; then
25- exec sudo -- " ${orig_argv[@]} "
26- else
27- exec su root -c " $( printf ' %q' " ${orig_argv[@]} " ) "
28- fi
23+ (( EUID == 0 )) && return
24+ if (( rootless_userns)) ; then
25+ exec become-root unshare --mount " ${orig_argv[@]} "
26+ elif type -P sudo > /dev/null; then
27+ exec sudo -- " ${orig_argv[@]} "
28+ else
29+ exec su root -c " $( printf ' %q' " ${orig_argv[@]} " ) "
30+ fi
31+ }
32+
33+ function require_userns_tools() {
34+ if command -v become-root > /dev/null \
35+ && command -v nsjail > /dev/null \
36+ && command -v fuse-overlayfs > /dev/null
37+ then
38+ return 0
39+ fi
40+ warning " nsjail, fuse-overlayfs and become-root are necessary for rootless operation"
41+ warning " https://github.com/giuseppe/become-root"
42+ warning " https://github.com/containers/fuse-overlayfs"
43+ warning " https://github.com/google/nsjail"
44+ return 1
45+ }
46+
47+ function mountoverlay() {
48+ if (( rootless_userns)) ; then
49+ fuse-overlayfs " $@ "
50+ else
51+ mount -t overlayfs overlayfs " $@ "
52+ fi
53+ }
54+ function umountoverlay() {
55+ if (( rootless_userns)) ; then
56+ fusermount -u " $@ "
57+ else
58+ umount " $@ "
59+ fi
2960}
3061
3162# Use a private gpg keyring
@@ -36,58 +67,58 @@ function gpg() {
3667function init_gnupg() {
3768 [ ! -d " $BUILDDIRECTORY /_gnupg" ] && mkdir -p " $BUILDDIRECTORY /_gnupg"
3869
39- # ensure signing key is available
40- gpg --auto-key-locate nodefault,wkd --locate-keys
[email protected] 70+ # ensure signing key is available
71+ gpg --auto-key-locate nodefault,wkd --locate-keys
[email protected] 4172}
4273
4374# Desc: Sets the appropriate colors for output
4475function colorize() {
45- # prefer terminal safe colored and bold text when tput is supported
46- if tput setaf 0 & > /dev/null; then
47- ALL_OFF=" $( tput sgr0) "
48- BOLD=" $( tput bold) "
49- BLUE=" ${BOLD} $( tput setaf 4) "
50- GREEN=" ${BOLD} $( tput setaf 2) "
51- RED=" ${BOLD} $( tput setaf 1) "
52- YELLOW=" ${BOLD} $( tput setaf 3) "
53- else
54- ALL_OFF=" \e[0m"
55- BOLD=" \e[1m"
56- BLUE=" ${BOLD} \e[34m"
57- GREEN=" ${BOLD} \e[32m"
58- RED=" ${BOLD} \e[31m"
59- YELLOW=" ${BOLD} \e[33m"
60- fi
61- readonly ALL_OFF BOLD BLUE GREEN RED YELLOW
76+ # prefer terminal safe colored and bold text when tput is supported
77+ if tput setaf 0 & > /dev/null; then
78+ ALL_OFF=" $( tput sgr0) "
79+ BOLD=" $( tput bold) "
80+ BLUE=" ${BOLD} $( tput setaf 4) "
81+ GREEN=" ${BOLD} $( tput setaf 2) "
82+ RED=" ${BOLD} $( tput setaf 1) "
83+ YELLOW=" ${BOLD} $( tput setaf 3) "
84+ else
85+ ALL_OFF=" \e[0m"
86+ BOLD=" \e[1m"
87+ BLUE=" ${BOLD} \e[34m"
88+ GREEN=" ${BOLD} \e[32m"
89+ RED=" ${BOLD} \e[31m"
90+ YELLOW=" ${BOLD} \e[33m"
91+ fi
92+ readonly ALL_OFF BOLD BLUE GREEN RED YELLOW
6293}
6394colorize
6495
6596# Desc: Message format
6697function msg() {
67- local mesg=$1 ; shift
98+ local mesg=$1 ; shift
6899 # shellcheck disable=SC2059
69- printf " ${GREEN} ==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF} \n" " $@ " >&2
100+ printf " ${GREEN} ==>${ALL_OFF}${BOLD} ${mesg}${ALL_OFF} \n" " $@ " >&2
70101}
71102
72103# Desc: Sub-message format
73104function msg2() {
74- local mesg=$1 ; shift
105+ local mesg=$1 ; shift
75106 # shellcheck disable=SC2059
76- printf " ${BLUE} ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF} \n" " $@ " >&2
107+ printf " ${BLUE} ->${ALL_OFF}${BOLD} ${mesg}${ALL_OFF} \n" " $@ " >&2
77108}
78109
79110# Desc: Warning format
80111function warning() {
81- local mesg=$1 ; shift
112+ local mesg=$1 ; shift
82113 # shellcheck disable=SC2059
83- printf " ${YELLOW} ==> $( gettext " WARNING:" ) ${ALL_OFF}${BOLD} ${mesg}${ALL_OFF} \n" " $@ " >&2
114+ printf " ${YELLOW} ==> $( gettext " WARNING:" ) ${ALL_OFF}${BOLD} ${mesg}${ALL_OFF} \n" " $@ " >&2
84115}
85116
86117# Desc: Error format
87118function error() {
88- local mesg=$1 ; shift
119+ local mesg=$1 ; shift
89120 # shellcheck disable=SC2059
90- printf " ${RED} ==> $( gettext " ERROR:" ) ${ALL_OFF}${BOLD} ${mesg}${ALL_OFF} \n" " $@ " >&2
121+ printf " ${RED} ==> $( gettext " ERROR:" ) ${ALL_OFF}${BOLD} ${mesg}${ALL_OFF} \n" " $@ " >&2
91122}
92123
93124# #
@@ -134,11 +165,42 @@ lock_close() {
134165function exec_nspawn(){
135166 local container=$1
136167 systemd-nspawn -q \
137- --as-pid2 \
138- --register=no \
139- --pipe \
140- -E " PATH=/usr/local/sbin:/usr/local/bin:/usr/bin" \
141- -D " $BUILDDIRECTORY /$container " " ${@: 2} "
168+ --as-pid2 \
169+ --register=no \
170+ --pipe \
171+ -E " PATH=/usr/local/sbin:/usr/local/bin:/usr/bin" \
172+ -D " $BUILDDIRECTORY /$container " " ${@: 2} "
173+ }
174+ # Desc: Executes an command inside a given nsjail container
175+ # 1: Container name
176+ # 2: Optional: one --bind=... bindmount option
177+ # 2/3: Command to execute
178+ function exec_nsjail(){
179+ local container=$1
180+ local args=( -Mo --quiet
181+ --disable_clone_newuser
182+ --disable_clone_newnet
183+ --disable_rlimits
184+ --keep_caps
185+ --tmpfsmount /tmp
186+ --bindmount_ro /dev
187+ -E " PATH=/usr/local/sbin:/usr/local/bin:/usr/bin"
188+ --chroot " $BUILDDIRECTORY /$container " --rw
189+ )
190+ # # use a no-op forking unshare as pid1
191+ if [[ $2 == --bind= * ]] ; then
192+ nsjail " ${args[@]} " --bindmount " ${2# --bind=} " -- /usr/bin/unshare -f " ${@: 3} "
193+ else
194+ nsjail " ${args[@]} " -- /usr/bin/unshare -f " ${@: 2} "
195+ fi
196+ }
197+
198+ function exec_container(){
199+ if (( rootless_userns)) ; then
200+ exec_nsjail " $@ "
201+ else
202+ exec_nspawn " $@ "
203+ fi
142204}
143205
144206# Desc: Removes the root container
@@ -152,7 +214,7 @@ function cleanup_root_volume(){
152214function remove_snapshot (){
153215 local build=$1
154216 msg2 " Delete snapshot for $build ..."
155- umount " $BUILDDIRECTORY /$build " || true
217+ umountoverlay " $BUILDDIRECTORY /$build " || true
156218 rm -rf " ${BUILDDIRECTORY:? } /${build} "
157219 rm -rf " ${BUILDDIRECTORY:? } /${build} _upperdir"
158220 rm -rf " ${BUILDDIRECTORY:? } /${build} _workdir"
@@ -169,7 +231,7 @@ function create_snapshot (){
169231 msg2 " Create snapshot for $build ..."
170232 mkdir -p " $BUILDDIRECTORY /" {" ${build} " ," ${build} _upperdir" ," ${build} _workdir" }
171233 # shellcheck disable=SC2140
172- mount -t overlay overlay \
234+ mountoverlay \
173235 -o lowerdir=" $BUILDDIRECTORY /root" ,upperdir=" $BUILDDIRECTORY /${build} _upperdir" ,workdir=" $BUILDDIRECTORY /${build} _workdir" \
174236 " $BUILDDIRECTORY /${build} "
175237 touch " $BUILDDIRECTORY /$build "
@@ -181,7 +243,7 @@ function create_snapshot (){
181243function build_package(){
182244 local build=$1
183245 local builddir=${2:- " /startdir" }
184- exec_nspawn " $build " \
246+ exec_container " $build " \
185247 --bind=" $PWD :/srcdest" \
186248bash << -__END__
187249set -e
@@ -205,6 +267,7 @@ function init_chroot(){
205267
206268 # Prepare root chroot
207269 if [ ! -d " $BUILDDIRECTORY " /root ]; then
270+
208271 lock 9 " $BUILDDIRECTORY " /root.lock
209272 msg " Preparing chroot"
210273 trap ' { cleanup_root_volume; exit 1; }' ERR
@@ -218,30 +281,30 @@ function init_chroot(){
218281 printf ' %s.UTF-8 UTF-8\n' en_US de_DE > " $BUILDDIRECTORY " /root/etc/locale.gen
219282 printf ' LANG=en_US.UTF-8\n' > " $BUILDDIRECTORY " /root/etc/locale.conf
220283
221- systemd-machine-id-setup --root= " $BUILDDIRECTORY " /root
284+ exec_container root systemd-machine-id-setup
222285 msg2 " Setting up keyring, this might take a while..."
223- exec_nspawn root pacman-key --init & > /dev/null
224- exec_nspawn root pacman-key --populate archlinux & > /dev/null
286+ exec_container root pacman-key --init & > /dev/null
287+ exec_container root pacman-key --populate archlinux & > /dev/null
225288
226289 msg2 " Updating and installing base & base-devel"
227- exec_nspawn root pacman -Syu base-devel --noconfirm
228- exec_nspawn root pacman -R arch-install-scripts --noconfirm
229- exec_nspawn root locale-gen
290+ exec_container root pacman -Syu base-devel --noconfirm
291+ exec_container root pacman -R arch-install-scripts --noconfirm
292+ exec_container root locale-gen
230293
231294 printf ' builduser ALL = NOPASSWD: /usr/bin/pacman\n' > " $BUILDDIRECTORY " /root/etc/sudoers.d/builduser-pacman
232- exec_nspawn root useradd -m -G wheel -s /bin/bash -d /build builduser
295+ exec_container root useradd -m -G wheel -s /bin/bash -d /build builduser
233296 echo " keyserver-options auto-key-retrieve" | install -Dm644 /dev/stdin " $BUILDDIRECTORY /root" /build/.gnupg/gpg.conf
234- exec_nspawn root chown -R builduser /build/.gnupg
297+ exec_container root chown -R builduser /build/.gnupg
235298 lock_close 9
236299 else
237-
238300 if lock 9 " $BUILDDIRECTORY " /root.lock; then
239- printf ' Server = %s\n' " $HOSTMIRROR " > " $BUILDDIRECTORY " /root/etc/pacman.d/mirrorlist
240- exec_nspawn root pacman -Syu --noconfirm
301+ printf ' Server = %s\n' " $HOSTMIRROR " > " $BUILDDIRECTORY " /root/etc/pacman.d/mirrorlist
302+ exec_container root pacman -Syu --noconfirm
241303 lock_close 9
242304 else
243305 msg " Couldn't acquire lock on root chroot, didn't update."
244- fi
306+ fi
307+
245308 fi
246309 trap - ERR INT
247310}
@@ -276,7 +339,6 @@ function cmd_check(){
276339 pkgbuild_sha256sum= " ${buildinfo[pkgbuild_sha256sum]} "
277340 SOURCE_DATE_EPOCH= " ${buildinfo[builddate]} "
278341
279-
280342 local build= " ${pkgbase} _$$ "
281343
282344 msg2 " Preparing packages"
@@ -304,7 +366,7 @@ function cmd_check(){
304366 sed -i " s/LocalFileSigLevel.*//g" " $BUILDDIRECTORY /$build /etc/pacman.conf"
305367
306368 # Father I have sinned
307- exec_nspawn " $build " \
369+ exec_container " $build " \
308370 bash << -__END__
309371shopt -s globstar
310372install -d -o builduser -g builduser /startdir
@@ -326,16 +388,16 @@ __END__
326388
327389 msg " Installing packages"
328390 # shellcheck disable=SC2086
329- exec_nspawn " $build " --bind= " $( readlink -e ${cachedir} ) :/cache" bash -c \
391+ exec_container " $build " --bind= " $( readlink -e ${cachedir} ) :/cache" bash -c \
330392 ' yes y | pacman -Udd --overwrite "*" -- "$@"' -b ash " ${packages[@]} "
331393
332394 read -r -a buildinfo_packages <<< " $( buildinfo -f installed " ${pkg} " ) "
333395 uninstall= $( comm -13 \
334396 <( printf ' %s\n' " ${buildinfo_packages[@]} " | rev | cut -d- -f4- | rev | sort) \
335- <( exec_nspawn " $build " --bind=" $( readlink -e ${cachedir} ) :/cache" pacman -Qq | sort) )
397+ <( exec_container " $build " --bind=" $( readlink -e ${cachedir} ) :/cache" pacman -Qq | sort) )
336398
337399 if [ -n " $uninstall " ]; then
338- exec_nspawn " $build " pacman -Rdd --noconfirm -- $uninstall
400+ exec_container " $build " pacman -Rdd --noconfirm -- $uninstall
339401 fi
340402
341403 build_package " $build " " $builddir "
392454
393455xdg_repro_dir= " ${XDG_CONFIG_HOME:- $HOME / .config} /archlinux-repro"
394456if [[ -r " $xdg_repro_dir /repro.conf" ]]; then
395- # shellcheck source=/dev/null
396- source " $xdg_repro_dir /repro.conf"
457+ # shellcheck source=/dev/null
458+ source " $xdg_repro_dir /repro.conf"
397459elif [[ -r " $HOME /.repro.conf" ]]; then
398- # shellcheck source=/dev/null
399- source " $HOME /.repro.conf"
460+ # shellcheck source=/dev/null
461+ source " $HOME /.repro.conf"
400462fi
401463
402464
403- while getopts :hdoC :P:M: arg; do
465+ while getopts :hdorC :P:M: arg; do
404466 case $arg in
405467 h) print_help; exit 0;;
406468 d) run_diffoscope=1;;
469+ r) rootless_userns=1;
470+ require_userns_tools || exit 1
471+ # TODO: better detection for valid writable build directory
472+ [[ $BUILDDIRECTORY == /var/lib/repro ]] && BUILDDIRECTORY=" ${XDG_CACHE_HOME:- $HOME / .cache} /archlinux-repro"
473+ ;;
407474 * ) ;;
408475 esac
409476done
0 commit comments