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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
#!/bin/sh
#
# Copyright 2020 Johannes Schauer Marin Rodrigues <josch@debian.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# this script is part of debbisect and usually called by debbisect itself
#
# it accepts eight or ten arguments:
# 1. dependencies
# 2. script name or shell snippet
# 3. mirror URL
# 4. architecture
# 5. suite
# 6. components
# 7. memsize
# 8. disksize
# 9. (optional) second mirror URL
# 10. (optional) package to upgrade
#
# It will create an ephemeral qemu virtual machine using mmdebstrap and
# guestfish using (3.) as mirror, (4.) as architecture, (5.) as suite and
# (6.) as components, install the dependencies given in (1.) and execute the
# script given in (2.).
# Its output is the exit code of the script as well as a file ./pkglist
# containing the output of "dpkg-query -W" inside the chroot.
#
# If not only six but eight arguments are given, then the second mirror URL
# (9.) will be added to the apt sources and the single package (10.) will be
# upgraded to its version from (9.).
#
# shellcheck disable=SC2016
set -exu
if [ $# -ne 8 ] && [ $# -ne 10 ]; then
echo "usage: $0 depends script mirror1 architecture suite components memsize disksize [mirror2 toupgrade]"
exit 1
fi
depends=$1
script=$2
mirror1=$3
architecture=$4
suite=$5
components=$6
memsize=$7
disksize=$8
if [ $# -eq 10 ]; then
mirror2=$9
toupgrade=${10}
fi
TMPDIR=$(mktemp --tmpdir --directory debbisect_qemu.XXXXXXXXXX)
cleantmp() {
for f in customize.sh id_rsa id_rsa.pub qemu.log config; do
rm -f "$TMPDIR/$f"
done
rmdir "$TMPDIR"
}
trap cleantmp EXIT
# the temporary directory must be world readable (for example in unshare mode)
chmod a+xr "$TMPDIR"
ssh-keygen -q -t rsa -f "$TMPDIR/id_rsa" -N ""
# The following hacks are needed to go back as far as 2006-08-10:
#
# - Acquire::Check-Valid-Until "false" allows Release files with an expired
# Valid-Until dates
# - Apt::Key::gpgvcommand allows expired GPG keys
# - Apt::Hashes::SHA1::Weak "yes" allows GPG keys with weak SHA1 signature
# - /usr/share/keyrings lets apt use debian-archive-removed-keys.gpg
# - /usr/share/mmdebstrap/hooks/jessie-or-older performs some setup that is
# only required for Debian Jessie or older
#
debvm-create --skip=usrmerge --size="$disksize" \
--sshkey="$TMPDIR/id_rsa.pub" --release="$suite" \
--output="debian-rootfs.img" -- \
--architecture="$architecture" \
--components="$components" \
--aptopt='Acquire::Check-Valid-Until "false"' \
--aptopt='Apt::Key::gpgvcommand "/usr/libexec/mmdebstrap/gpgvnoexpkeysig"' \
--aptopt='Apt::Hashes::SHA1::Weak "yes"' \
--keyring=/usr/share/keyrings \
--hook-dir=/usr/share/mmdebstrap/hooks/maybe-jessie-or-older \
--hook-dir=/usr/share/mmdebstrap/hooks/maybe-merged-usr \
--skip=check/signed-by \
"$mirror1"
timeout --kill-after=60s 60m \
debvm-run --image="debian-rootfs.img" \
--sshport=10022 -- \
-m "$memsize" \
-serial mon:stdio \
> "$TMPDIR/qemu.log" </dev/null 2>&1 &
# store the pid
QEMUPID=$!
# use a function here, so that we can properly quote the path to qemu.log
showqemulog() {
cat --show-nonprinting "$TMPDIR/qemu.log"
}
# show the log and kill qemu in case the script exits first
trap 'showqemulog; cleantmp; kill $QEMUPID' EXIT
# the default ssh command does not store known hosts and even ignores host keys
# it identifies itself with the rsa key generated above
# pseudo terminal allocation is disabled or otherwise, programs executed via
# ssh might wait for input on stdin of the ssh process
cat << END > "$TMPDIR/config"
Host qemu
Hostname 127.0.0.1
User root
Port 10022
UserKnownHostsFile /dev/null
StrictHostKeyChecking no
IdentityFile $TMPDIR/id_rsa
RequestTTY no
END
debvm-waitssh 10022
# we install dependencies now and not with mmdebstrap --include in case some
# dependencies require a full system present
if [ -n "$depends" ]; then
ssh -F "$TMPDIR/config" qemu apt-get update
# shellcheck disable=SC2046,SC2086
ssh -F "$TMPDIR/config" qemu env DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true apt-get --yes install --no-install-recommends $(echo $depends | tr ',' ' ')
fi
# in its ten-argument form, a single package has to be upgraded to its
# version from the first bad timestamp
if [ $# -eq 10 ]; then
# replace content of sources.list with first bad timestamp
mirror2=$(echo "$mirror2" | sed 's/http:\/\/127.0.0.1:/http:\/\/10.0.2.2:/')
echo "deb $mirror2 $suite $(echo "$components" | tr ',' ' ')" | ssh -F "$TMPDIR/config" qemu "cat > /etc/apt/sources.list"
ssh -F "$TMPDIR/config" qemu apt-get update
# upgrade a single package (and whatever else apt deems necessary)
before=$(ssh -F "$TMPDIR/config" qemu dpkg-query -W)
ssh -F "$TMPDIR/config" qemu env DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true apt-get --yes install --no-install-recommends "$toupgrade"
after=$(ssh -F "$TMPDIR/config" qemu dpkg-query -W)
# make sure that something was upgraded
if [ "$before" = "$after" ]; then
echo "nothing got upgraded -- this should never happen" >&2
exit 1
fi
ssh -F "$TMPDIR/config" qemu dpkg-query -W > "./debbisect.$DEBIAN_BISECT_TIMESTAMP.$toupgrade.pkglist"
else
ssh -F "$TMPDIR/config" qemu dpkg-query -W > "./debbisect.$DEBIAN_BISECT_TIMESTAMP.pkglist"
fi
ssh -F "$TMPDIR/config" qemu dpkg-query --list | cat
# explicitly export all necessary variables
# because we use set -u this also makes sure that this script has these
# variables set in the first place
export DEBIAN_BISECT_EPOCH="$DEBIAN_BISECT_EPOCH"
export DEBIAN_BISECT_TIMESTAMP="$DEBIAN_BISECT_TIMESTAMP"
if [ -z ${DEBIAN_BISECT_MIRROR+x} ]; then
# DEBIAN_BISECT_MIRROR was unset (caching is disabled)
true
else
# replace the localhost IP by the IP of the host as seen by qemu
DEBIAN_BISECT_MIRROR=$(echo "$DEBIAN_BISECT_MIRROR" | sed 's/http:\/\/127.0.0.1:/http:\/\/10.0.2.2:/')
export DEBIAN_BISECT_MIRROR="$DEBIAN_BISECT_MIRROR"
fi
# either execute $script as a script from $PATH or as a shell snippet
ret=0
if [ -x "$script" ] || echo "$script" | grep --invert-match --silent --perl-regexp '[^\w@\%+=:,.\/-]'; then
"$script" "$TMPDIR/config" || ret=$?
else
sh -c "$script" exec "$TMPDIR/config" || ret=$?
fi
# since we installed systemd-sysv, systemctl is available
ssh -F "$TMPDIR/config" qemu systemctl poweroff
wait $QEMUPID
trap - EXIT
showqemulog
cleantmp
if [ "$ret" -eq 0 ]; then
exit 0
else
exit 1
fi
|