pmbootstrap-meow/pmb/chroot/init.py
Caleb Connolly e547bb7f9c
helpers: mount: drop args (MR 2252)
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
2024-06-23 12:38:38 +02:00

206 lines
7.4 KiB
Python

# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
import enum
import filecmp
from typing import List
from pmb.helpers import logging
import os
import pmb.chroot
import pmb.chroot.apk_static
import pmb.config
import pmb.config.workdir
from pmb.core.types import PmbArgs
import pmb.helpers.repo
import pmb.helpers.run
import pmb.helpers.other
import pmb.parse.arch
from pmb.core import Chroot, ChrootType
cache_chroot_is_outdated: List[str] = []
class UsrMerge(enum.Enum):
"""
Merge /usr while initializing chroot.
https://systemd.io/THE_CASE_FOR_THE_USR_MERGE/
"""
AUTO = 0
ON = 1
OFF = 2
def copy_resolv_conf(args: PmbArgs, chroot: Chroot):
"""
Use pythons super fast file compare function (due to caching)
and copy the /etc/resolv.conf to the chroot, in case it is
different from the host.
If the file doesn't exist, create an empty file with 'touch'.
"""
host = "/etc/resolv.conf"
resolv_path = chroot / host
if os.path.exists(host):
if not resolv_path.exists() or not filecmp.cmp(host, resolv_path):
pmb.helpers.run.root(["cp", host, resolv_path])
else:
pmb.helpers.run.root(["touch", resolv_path])
def mark_in_chroot(args: PmbArgs, chroot: Chroot=Chroot.native()):
"""
Touch a flag so we can know when we're running in chroot (and
don't accidentally flash partitions on our host). This marker
gets removed in pmb.chroot.shutdown (pmbootstrap shutdown).
"""
in_chroot_file = chroot / "in-pmbootstrap"
if not in_chroot_file.exists():
pmb.helpers.run.root(["touch", in_chroot_file])
def setup_qemu_emulation(args: PmbArgs, chroot: Chroot):
arch = chroot.arch
if not pmb.parse.arch.cpu_emulation_required(arch):
return
arch_qemu = pmb.parse.arch.alpine_to_qemu(arch)
# mount --bind the qemu-user binary
pmb.chroot.binfmt.register(args, arch)
pmb.helpers.mount.bind_file(Chroot.native() / f"/usr/bin/qemu-{arch_qemu}",
chroot / f"usr/bin/qemu-{arch_qemu}-static",
create_folders=True)
def init_keys(args: PmbArgs):
"""
All Alpine and postmarketOS repository keys are shipped with pmbootstrap.
Copy them into $WORK/config_apk_keys, which gets mounted inside the various
chroots as /etc/apk/keys.
This is done before installing any package, so apk can verify APKINDEX
files of binary repositories even though alpine-keys/postmarketos-keys are
not installed yet.
"""
for key in pmb.config.apk_keys_path.glob("*.pub"):
target = pmb.config.work / "config_apk_keys" / key.name
if not target.exists():
# Copy as root, so the resulting files in chroots are owned by root
pmb.helpers.run.root(["cp", key, target])
def init_usr_merge(args: PmbArgs, chroot: Chroot):
logging.info(f"({chroot}) merge /usr")
script = f"{pmb.config.pmb_src}/pmb/data/merge-usr.sh"
pmb.helpers.run.root(["sh", "-e", script, "CALLED_FROM_PMB",
chroot.path])
def warn_if_chroot_is_outdated(args: PmbArgs, chroot: Chroot):
global cache_chroot_is_outdated
# Only check / display the warning once per session
if str(chroot) in cache_chroot_is_outdated:
return
if pmb.config.workdir.chroots_outdated(args, chroot):
days_warn = int(pmb.config.chroot_outdated / 3600 / 24)
logging.warning(f"WARNING: Your {chroot} chroot is older than"
f" {days_warn} days. Consider running"
" 'pmbootstrap zap'.")
cache_chroot_is_outdated += [str(chroot)]
def init(args: PmbArgs, chroot: Chroot=Chroot.native(), usr_merge=UsrMerge.AUTO,
postmarketos_mirror=True):
"""
Initialize a chroot by copying the resolv.conf and updating
/etc/apk/repositories. If /bin/sh is missing, create the chroot from
scratch.
:param usr_merge: set to ON to force having a merged /usr. With AUTO it is
only done if the user chose to install systemd in
pmbootstrap init.
:param postmarketos_mirror: add postmarketos mirror URLs
"""
# When already initialized: just prepare the chroot
arch = chroot.arch
already_setup = str(chroot) in pmb.helpers.other.cache["pmb.chroot.init"]
if already_setup:
logging.warning(f"({chroot}) FIXME! init() called multiple times!")
return
pmb.chroot.mount(args, chroot)
setup_qemu_emulation(args, chroot)
mark_in_chroot(args, chroot)
if (chroot / "bin/sh").is_symlink():
pmb.config.workdir.chroot_check_channel(args, chroot)
copy_resolv_conf(args, chroot)
pmb.chroot.apk.update_repository_list(args, chroot, postmarketos_mirror)
warn_if_chroot_is_outdated(args, chroot)
pmb.helpers.other.cache["pmb.chroot.init"][str(chroot)] = True
return
# Require apk-tools-static
pmb.chroot.apk_static.init(args)
logging.info(f"({chroot}) install alpine-base")
# Initialize cache
apk_cache = pmb.config.work / f"cache_apk_{arch}"
pmb.helpers.run.root(["ln", "-s", "-f", "/var/cache/apk",
chroot / "etc/apk/cache"])
# Initialize /etc/apk/keys/, resolv.conf, repositories
init_keys(args)
copy_resolv_conf(args, chroot)
pmb.chroot.apk.update_repository_list(args, chroot, postmarketos_mirror)
pmb.config.workdir.chroot_save_init(args, chroot)
# Install alpine-base
pmb.helpers.repo.update(args, arch)
pkgs = ["alpine-base"]
# install apk static in the native chroot so we can run it
# we have a forked apk for systemd and this is the easiest
# way to install/run it.
if chroot.type == ChrootType.NATIVE:
pkgs += ["apk-tools-static"]
pmb.chroot.apk_static.run(args, ["--root", chroot.path,
"--cache-dir", apk_cache,
"--initdb", "--arch", arch,
"add"] + pkgs)
# Merge /usr
if usr_merge is UsrMerge.AUTO and pmb.config.is_systemd_selected(args):
usr_merge = UsrMerge.ON
if usr_merge is UsrMerge.ON:
init_usr_merge(args, chroot)
# Building chroots: create "pmos" user, add symlinks to /home/pmos
if not chroot.type == ChrootType.ROOTFS:
pmb.chroot.root(args, ["adduser", "-D", "pmos", "-u",
pmb.config.chroot_uid_user],
chroot)
# Create the links (with subfolders if necessary)
for target, link_name in pmb.config.chroot_home_symlinks.items():
link_dir = os.path.dirname(link_name)
if not os.path.exists(chroot / link_dir):
pmb.chroot.user(args, ["mkdir", "-p", link_dir], chroot)
if not os.path.exists(chroot / target):
pmb.chroot.root(args, ["mkdir", "-p", target], chroot)
pmb.chroot.user(args, ["ln", "-s", target, link_name], chroot)
pmb.chroot.root(args, ["chown", "pmos:pmos", target], chroot)
# Upgrade packages in the chroot, in case alpine-base, apk, etc. have been
# built from source with pmbootstrap
command = ["--no-network", "upgrade", "-a"]
# Ignore missing repos before initial build (bpo#137)
if os.getenv("PMB_APK_FORCE_MISSING_REPOSITORIES") == "1":
command = ["--force-missing-repositories"] + command
pmb.chroot.root(args, ["apk"] + command, chroot)
pmb.helpers.other.cache["pmb.chroot.init"][str(chroot)]