blob: 4dcaa1ed493735f4d269119168a518310cbd7041 [file] [log] [blame]
#!/bin/bash
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
SCRIPT_ROOT=$(dirname $(readlink -f "$0"))
. "${SCRIPT_ROOT}/build_library/build_common.sh" || exit 1
assert_inside_chroot "$@"
DEFINE_string board "${DEFAULT_BOARD}" \
"The board to build an image for."
DEFINE_string package "" \
"Package whose bare minimum deps the final container should have."
DEFINE_string name "example" \
"Name of the container."
DEFINE_string extra "" \
"Comma separated extra packages to be included in the final image."
DEFINE_string argv "" \
"The command (and args) to run inside the container."
FLAGS_HELP="USAGE: $(basename "$0") [flags]
The --package and --argv options are required.
To build a container for fastboot, you might want something like:
$ $(basename "$0") --package dev-util/android-tools --argv /usr/bin/fastboot
"
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
switch_to_strict_mode
# Sign the manifest.json file.
sign_manifest() {
"${VBOOT_SIGNING_DIR}"/sign_official_build.sh \
oci-container "$1" "${VBOOT_DEVKEYS_DIR}" "$1"
}
# Generate the manifest.json for this container.
generate_manifest() {
local pkg_name="$1"
local output="$2"
local manifest="${output}/manifest.json"
(
hash() {
local file="$1"
local comma="$2"
# The Chromium base library does not support dots in dict keys. They
# use it as a short hand to access children dicts. Normalize them.
local dict_key="${file//./_}"
local size sha
size=$(stat -c %s "${file}")
sha=$(sha256sum <"${file}" | awk '{print $1}')
printf ' "%s": {\n "size": %s,\n "sha256": "%s"\n }%s\n' \
"${dict_key}" "${size}" "${sha}" "${comma}"
}
cd "${output}"
printf '{\n"version": 1,\n"files": {\n'
hash "config.json" ","
hash "${pkg_name}.squashfs" ""
printf '}\n}\n'
) >"${manifest}"
# Sanity check the generated manifest.
python -mjson.tool <"${manifest}" >/dev/null
sign_manifest "${output}"
}
# Sign the specified disk image using verity so we can load it with dm-verity
# at runtime. We don't allow algorithm selection -- sha1 should be good enough
# for everyone! :)
# Note: We write the verity command line to the "verity" variable as an output.
sign_disk_image() {
local img="$1"
local hashtree="${img}.hashtree"
# TODO: Add "salt=random" here.
verity=$(verity mode=create alg=sha256 payload="${img}" \
hashtree="${hashtree}")
verity=$(echo "${verity}" | sed -E -e 's:(ROOT|HASH)_DEV:@DEV@:g')
cat "${hashtree}" >>"${img}"
rm "${hashtree}"
}
# Produce the container image.
build_container_image() {
local pkg_name="$1"
local container_name="$2"
local argv="$3"
# For now we hardcode chronos.
local container_uid="1000"
local container_gid="1000"
local output="${PWD}/${container_name}"
local ROOTDIR="${output}/rootfs"
export INSTALL_MASK="${DEFAULT_INSTALL_MASK}"
mkdir -p "${output}"
install_with_root_deps "${ROOTDIR}" "${pkg_name}" sys-libs/gcc-libs \
${FLAGS_extra//,/ }
info "Installing C library ... "
install_libc "${ROOTDIR}"
info "Cleaning excess files ... "
sudo rm -rf "${ROOTDIR}"/var "${ROOTDIR}"/usr/lib*/gconv/ \
"${ROOTDIR}"/sbin/ldconfig
sudo find "${ROOTDIR}"/ -type d -depth -exec rmdir {} + 2>/dev/null || :
info "Creating top level dirs and socket dirs ... "
sudo mkdir -p "${ROOTDIR}"/{dev,proc,root,sys,home/user,run,tmp,var}
info "Generating squashfs file ... "
local args=(
-all-root
-noappend
)
local img="${output}/${container_name}.squashfs"
# We need to run through sudo because some files might be read-only by root.
sudo mksquashfs "${ROOTDIR}" "${img}" "${args[@]}"
sudo chown $(id -u):$(id -g) "${img}"
info "Signing squashfs file ... "
local verity
sign_disk_image "${img}"
info "Adding config.json ... "
sed \
-e "s:@APP_NAME@:${container_name}:g" \
-e "s:@TERMINAL@:true:g" \
-e "s:@USER_UID@:${container_uid}:g" \
-e "s:@USER_GID@:${container_gid}:g" \
-e "s:@ARGV@:${argv// /\", \"}:g" \
-e "s:@VERITY@:${verity}:" \
generic_container_files/config.json >"${output}/config.json"
info "Generating manifest ..."
generate_manifest "${container_name}" "${output}"
info "Cleaning up ... "
sudo rm -rf "${ROOTDIR}"
mkdir "${ROOTDIR}"
}
run_emerge() {
emerge-${BOARD} \
--quiet --jobs ${NUM_JOBS} \
--usepkgonly \
"$@"
}
# Normal emerge.
install_with_no_deps() {
local root_dir="$1"
shift
info "Installing '$*' with no deps ... "
run_emerge --root="${root_dir}" "$@" --nodeps
}
# Emerge with root deps.
install_with_root_deps() {
local root_dir="$1"
shift
info "Installing '$*' with root deps ... "
run_emerge --root="${root_dir}" "$@" --root-deps=rdeps
}
main() {
. "${BUILD_LIBRARY_DIR}/board_options.sh" || exit 1
. "${BUILD_LIBRARY_DIR}/base_image_util.sh" || exit 1
if [[ -z "${FLAGS_argv}" ]]; then
die_notrace "--argv is needed."
fi
if [[ -z "${FLAGS_package}" ]]; then
die_notrace "--package is needed."
fi
local container_name="${FLAGS_name}"
if [[ -d "${container_name}" ]]; then
die_notrace "Output dir already exists : ${container_name}/"
fi
info "Building container '${container_name}' for ${FLAGS_package} ..."
build_container_image "${FLAGS_package}" "${container_name}" "${FLAGS_argv}"
}
main "$@"