OLD | NEW |
| (Empty) |
1 #!/bin/bash | |
2 | |
3 # Copyright (c) 2009 The Chromium OS Authors. All rights reserved. | |
4 # Use of this source code is governed by a BSD-style license that can be | |
5 # found in the LICENSE file. | |
6 | |
7 # Script to install packages into the target root file system. | |
8 # | |
9 # NOTE: This script should be called by build_image.sh. Do not run this | |
10 # on your own unless you know what you are doing. | |
11 | |
12 # Load common constants. This should be the first executable line. | |
13 # The path to common.sh should be relative to your script's location. | |
14 . "$(dirname "$0")/common.sh" | |
15 | |
16 # Script must be run inside the chroot | |
17 assert_inside_chroot | |
18 assert_not_root_user | |
19 | |
20 DEFAULT_PKGLIST="${SRC_ROOT}/package_repo/package-list-prod.txt" | |
21 | |
22 # Flags | |
23 DEFINE_string output_dir "" \ | |
24 "The location of the output directory to use [REQUIRED]." | |
25 DEFINE_string root "" \ | |
26 "The root file system to install packages in." | |
27 DEFINE_string arch "x86" \ | |
28 "The target architecture to build for. One of { x86, armel }." | |
29 DEFINE_string build_root "$DEFAULT_BUILD_ROOT" \ | |
30 "Root of build output" | |
31 DEFINE_string package_list "$DEFAULT_PKGLIST" \ | |
32 "Comma separated set of package-list files to use." | |
33 DEFINE_string mirror "$DEFAULT_IMG_MIRROR" \ | |
34 "The upstream package mirror to use." | |
35 DEFINE_string suite "$DEFAULT_IMG_SUITE" \ | |
36 "The upstream package suite to use." | |
37 DEFINE_string mirror2 "" "Additional package mirror to use (URL only)." | |
38 DEFINE_string suite2 "" "Package suite for additional mirror." | |
39 | |
40 # Parse command line | |
41 FLAGS "$@" || exit 1 | |
42 eval set -- "${FLAGS_ARGV}" | |
43 | |
44 # Die on any errors. | |
45 set -e | |
46 | |
47 KERNEL_DEB_PATH=$(find "${FLAGS_build_root}/${FLAGS_arch}/local_packages" \ | |
48 -name "linux-image-*.deb") | |
49 KERNEL_DEB=$(basename "${KERNEL_DEB_PATH}" .deb | sed -e 's/linux-image-//' \ | |
50 -e 's/_.*//') | |
51 KERNEL_VERSION=${KERNEL_VERSION:-${KERNEL_DEB}} | |
52 | |
53 if [[ -z "$FLAGS_output_dir" ]]; then | |
54 echo "Error: --output_dir is required." | |
55 exit 1 | |
56 fi | |
57 OUTPUT_DIR=$(readlink -f "$FLAGS_output_dir") | |
58 SETUP_DIR="${OUTPUT_DIR}/local_repo" | |
59 ROOT_FS_DIR="${OUTPUT_DIR}/rootfs" | |
60 if [[ -n "$FLAGS_root" ]]; then | |
61 ROOT_FS_DIR=$(readlink -f "$FLAGS_root") | |
62 fi | |
63 mkdir -p "$OUTPUT_DIR" "$SETUP_DIR" "$ROOT_FS_DIR" | |
64 | |
65 # Make sure anything mounted in the rootfs is cleaned up ok on exit. | |
66 cleanup_rootfs_mounts() { | |
67 # Occasionally there are some daemons left hanging around that have our | |
68 # root image file system open. We do a best effort attempt to kill them. | |
69 PIDS=`sudo lsof -t "$ROOT_FS_DIR" | sort | uniq` | |
70 for pid in $PIDS | |
71 do | |
72 local cmdline=`cat /proc/$pid/cmdline` | |
73 echo "Killing process that has open file on our rootfs: $cmdline" | |
74 ! sudo kill $pid # Preceded by ! to disable ERR trap. | |
75 done | |
76 | |
77 # Sometimes the volatile directory is left mounted and sometimes it is not, | |
78 # so we precede by '!' to disable the ERR trap. | |
79 ! sudo umount "$ROOT_FS_DIR"/lib/modules/2.6.*/volatile/ > /dev/null 2>&1 | |
80 | |
81 sudo umount "${ROOT_FS_DIR}/proc" | |
82 } | |
83 | |
84 # Set up repository for locally built packages; these take highest precedence. | |
85 mkdir -p "${SETUP_DIR}/local_packages" | |
86 cp "${FLAGS_build_root}/${FLAGS_arch}/local_packages"/*.deb \ | |
87 "${SETUP_DIR}/local_packages" | |
88 cd "$SETUP_DIR" | |
89 dpkg-scanpackages local_packages/ /dev/null | \ | |
90 gzip > local_packages/Packages.gz | |
91 cd - | |
92 | |
93 # Create the temporary apt source.list used to install packages. | |
94 APT_SOURCE="${OUTPUT_DIR}/sources.list" | |
95 cat <<EOF > "$APT_SOURCE" | |
96 deb copy:"$SETUP_DIR" local_packages/ | |
97 deb $FLAGS_mirror $FLAGS_suite main restricted multiverse universe | |
98 EOF | |
99 if [ -n "$FLAGS_mirror2" ] && [ -n "$FLAGS_suite2" ]; then | |
100 cat <<EOF >> "$APT_SOURCE" | |
101 deb $FLAGS_mirror2 $FLAGS_suite2 main restricted multiverse universe | |
102 EOF | |
103 fi | |
104 # Look for official file and use it if it exists | |
105 if [ -f ${SRC_ROOT}/package_repo/sources-official.list ]; then | |
106 cat ${SRC_ROOT}/package_repo/sources-official.list >> "$APT_SOURCE" | |
107 fi | |
108 | |
109 # Cache directory for APT to use. This cache is re-used across builds. We | |
110 # rely on the cache to reduce traffic to the hosted repositories. | |
111 APT_CACHE_DIR="${FLAGS_build_root}/apt_cache-${FLAGS_arch}/" | |
112 mkdir -p "${APT_CACHE_DIR}/archives/partial" | |
113 | |
114 if [ "${FLAGS_arch}" = x86 ]; then | |
115 APT_ARCH=i386 | |
116 else | |
117 APT_ARCH="${FLAGS_arch}" | |
118 fi | |
119 | |
120 # Create the apt configuration file. See "man apt.conf" | |
121 APT_PARTS="${OUTPUT_DIR}/apt.conf.d" | |
122 mkdir -p "$APT_PARTS" # An empty apt.conf.d to avoid other configs. | |
123 export APT_CONFIG="${OUTPUT_DIR}/apt.conf" | |
124 cat <<EOF > "$APT_CONFIG" | |
125 APT | |
126 { | |
127 Install-Recommends "0"; | |
128 Install-Suggests "0"; | |
129 Get | |
130 { | |
131 Assume-Yes "1"; | |
132 AllowUnauthenticated "1"; | |
133 }; | |
134 Architecture "${APT_ARCH}"; | |
135 }; | |
136 Dir | |
137 { | |
138 Bin { | |
139 dpkg "${SCRIPTS_DIR}/dpkg_no_scripts.sh"; | |
140 }; | |
141 Cache "$APT_CACHE_DIR"; | |
142 Cache { | |
143 archives "${APT_CACHE_DIR}/archives"; | |
144 }; | |
145 Etc | |
146 { | |
147 sourcelist "$APT_SOURCE"; | |
148 parts "$APT_PARTS"; | |
149 }; | |
150 State "${ROOT_FS_DIR}/var/lib/apt/"; | |
151 State | |
152 { | |
153 status "${ROOT_FS_DIR}/var/lib/dpkg/status"; | |
154 }; | |
155 }; | |
156 DPkg | |
157 { | |
158 options {"--root=${ROOT_FS_DIR}";}; | |
159 }; | |
160 EOF | |
161 | |
162 # TODO: Full audit of the apt conf dump to make sure things are ok. | |
163 apt-config dump > "${OUTPUT_DIR}/apt.conf.dump" | |
164 | |
165 # We do a rough equivalent to debootstrap that installs the minimal | |
166 # packages needed to be able to run apt to install the rest. We don't | |
167 # use debootstrap since it is geared toward having a second stage that | |
168 # needs to run package maintainer scripts. This is also simpler. | |
169 | |
170 # The set of required packages before apt can take over. | |
171 PACKAGES="debconf libacl1 libattr1 libc6 libgcc1 libselinux1" | |
172 | |
173 # Set of packages that we need to install early so that other packages | |
174 # maintainer scripts can still basically run. | |
175 # | |
176 # login - So that groupadd will work | |
177 # base-passwd/passwd - So that chmod and useradd/groupadd will work | |
178 # bash - So that scripts can run | |
179 # libpam-runtime/libuuid1 - Not exactly sure why | |
180 # sysv-rc - So that we can overwrite invoke-rc.d, update-rc.d | |
181 EXTRA_PACKAGES="base-files base-passwd bash libpam-runtime libuuid1 login passwd
sysv-rc mawk" | |
182 | |
183 # Prep the rootfs to work with dpgk and apt | |
184 sudo mkdir -p "${ROOT_FS_DIR}/var/lib/dpkg/info" | |
185 sudo touch "${ROOT_FS_DIR}/var/lib/dpkg/available" \ | |
186 "${ROOT_FS_DIR}/var/lib/dpkg/diversions" \ | |
187 "${ROOT_FS_DIR}/var/lib/dpkg/status" | |
188 sudo mkdir -p "${ROOT_FS_DIR}/var/lib/apt/lists/partial" \ | |
189 "${ROOT_FS_DIR}/var/lib/dpkg/updates" | |
190 | |
191 # Download the initial packages into the apt cache if necessary. | |
192 REPO="${APT_CACHE_DIR}/archives" | |
193 sudo APT_CONFIG="$APT_CONFIG" DEBIAN_FRONTEND=noninteractive apt-get update | |
194 sudo APT_CONFIG="$APT_CONFIG" DEBIAN_FRONTEND=noninteractive \ | |
195 apt-get --download-only install $PACKAGES $EXTRA_PACKAGES | |
196 | |
197 # Install initial packages directly with dpkg_no_scripts.sh | |
198 ARCH="$FLAGS_arch" | |
199 if [ "$ARCH" = "x86" ]; then | |
200 ARCH="i?86" # Match i386 | i686 | |
201 fi | |
202 for p in $PACKAGES $EXTRA_PACKAGES; do | |
203 PKG=$(ls "${REPO}"/${p}_*_$ARCH.deb || /bin/true) | |
204 if [ -z "$PKG" ]; then | |
205 PKG=$(ls "${REPO}"/${p}_*_all.deb) | |
206 fi | |
207 sudo ARCH="$FLAGS_arch" "${SCRIPTS_DIR}"/dpkg_no_scripts.sh \ | |
208 --root="$ROOT_FS_DIR" --nodpkg_fallback --unpack "$PKG" | |
209 sudo ARCH="$FLAGS_arch" "${SCRIPTS_DIR}"/dpkg_no_scripts.sh \ | |
210 --root="$ROOT_FS_DIR" --nodpkg_fallback --configure "$p" | |
211 done | |
212 | |
213 # Make sure that apt is ready to work. We use --fix-broken to trigger apt | |
214 # to install additional critical packages. If there are any of these, we | |
215 # disable the maintainer scripts so they install ok. | |
216 TMP_FORCE_NO_SCRIPTS="-o=DPkg::options::=--nodpkg_fallback" | |
217 sudo APT_CONFIG="$APT_CONFIG" DEBIAN_FRONTEND=noninteractive ARCH="$FLAGS_arch"\ | |
218 apt-get $TMP_FORCE_NO_SCRIPTS --force-yes --fix-broken install | |
219 | |
220 # TODO: Remove these hacks when we stop having maintainer scripts altogether. | |
221 sudo cp -a /dev/* "${ROOT_FS_DIR}/dev" | |
222 sudo cp -a /etc/resolv.conf "${ROOT_FS_DIR}/etc/resolv.conf" | |
223 sudo ln -sf /bin/true "${ROOT_FS_DIR}/usr/sbin/invoke-rc.d" | |
224 sudo ln -sf /bin/true "${ROOT_FS_DIR}/usr/sbin/update-rc.d" | |
225 | |
226 # Set up mounts for working within the rootfs. We copy some basic | |
227 # network information from the host so that maintainer scripts can | |
228 # access the network as needed. | |
229 # TODO: All of this rootfs mount stuff can be removed as soon as we stop | |
230 # running the maintainer scripts on install. | |
231 sudo mount -t proc proc "${ROOT_FS_DIR}/proc" | |
232 trap cleanup_rootfs_mounts EXIT | |
233 | |
234 filter_pkgs() { | |
235 pkglist="$1" | |
236 arch="$2" | |
237 | |
238 # to read list of package + version skipping empty lines and comments, and | |
239 # convert "foo 1.2-3" to "foo=1.2-3", use: | |
240 #pkgs="$(grep '^[^#]' "$pkglist" | cut -d ' ' -f 1-2 | sed 's/ /=/')" | |
241 | |
242 # read list of "package [optional arch list]" skipping empty lines and | |
243 # comments | |
244 grep '^[^#]' "$pkglist" | while read pkg archspec; do | |
245 case "$archspec" in | |
246 ""|"[$arch "*|"[$arch]"|*" $arch]") | |
247 echo "$pkg" | |
248 ;; | |
249 *"!$arch "*|*"!$arch]") | |
250 : | |
251 ;; | |
252 "["*"!"*"]") | |
253 echo "$pkg" | |
254 ;; | |
255 esac | |
256 done | |
257 } | |
258 | |
259 # Install packages from the given package-lists | |
260 PACKAGE_LISTS=$(echo "$FLAGS_package_list" | sed -e 's/,/ /g') | |
261 PKG_LIST_ARCH="$FLAGS_arch" | |
262 if [ "$PKG_LIST_ARCH" = "x86" ]; then | |
263 PKG_LIST_ARCH="i386" | |
264 FORCE_NO_SCRIPTS="" | |
265 else | |
266 # For armel we forcefully disable all maintainer scripts. | |
267 # TODO: Remove this when everything is whitelisted for all build variants. | |
268 FORCE_NO_SCRIPTS="-o=DPkg::options::=--nodpkg_fallback" | |
269 fi | |
270 for p in $PACKAGE_LISTS; do | |
271 COMPONENTS=$(filter_pkgs "$p" "$PKG_LIST_ARCH") | |
272 sudo APT_CONFIG="$APT_CONFIG" DEBIAN_FRONTEND=noninteractive \ | |
273 ARCH="$FLAGS_arch" \ | |
274 apt-get $FORCE_NO_SCRIPTS --force-yes install $COMPONENTS | |
275 done | |
276 | |
277 # Create kernel installation configuration to suppress warnings, | |
278 # install the kernel in /boot, and manage symlinks. | |
279 cat <<EOF | sudo dd of="${ROOT_FS_DIR}/etc/kernel-img.conf" | |
280 link_in_boot = yes | |
281 do_symlinks = yes | |
282 minimal_swap = yes | |
283 clobber_modules = yes | |
284 warn_reboot = no | |
285 do_bootloader = no | |
286 do_initrd = yes | |
287 warn_initrd = no | |
288 EOF | |
289 | |
290 # Install the kernel. | |
291 # TODO: Support for armel kernels. | |
292 if [ "$FLAGS_arch" = "x86" ]; then | |
293 sudo APT_CONFIG="$APT_CONFIG" DEBIAN_FRONTEND=noninteractive ARCH="$FLAGS_arch
"\ | |
294 apt-get --force-yes install "linux-image-${KERNEL_VERSION}" | |
295 fi | |
296 | |
297 # Install optionally present rootfs static data. This can be used to blast | |
298 # custom firmware, kernel modules, etc. onto the image. | |
299 # TODO: Remove this hack at some point. | |
300 LOCAL_ASSETS="${FLAGS_build_root}/${FLAGS_arch}/local_assets" | |
301 OPTIONAL_ROOTFS_DATA="${LOCAL_ASSETS}/rootfs_data.tgz" | |
302 if [ -f "${OPTIONAL_ROOTFS_DATA}" ]; then | |
303 sudo tar -zxvf "${OPTIONAL_ROOTFS_DATA}" -C "${ROOT_FS_DIR}" | |
304 fi | |
305 | |
306 # List all packages installed so far, since these are what the local | |
307 # repository needs to contain. | |
308 # TODO: Replace with list_installed_packages.sh when it is fixed up. | |
309 dpkg --root="${ROOT_FS_DIR}" -l > \ | |
310 "${OUTPUT_DIR}/package_list_installed.txt" | |
311 | |
312 cleanup_rootfs_mounts | |
313 trap - EXIT | |
OLD | NEW |