| #!/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 "$@" |