2017-05-26 20:08:45 +00:00
|
|
|
"""
|
|
|
|
Copyright 2017 Oliver Smith
|
|
|
|
|
|
|
|
This file is part of pmbootstrap.
|
|
|
|
|
|
|
|
pmbootstrap is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
pmbootstrap is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
import glob
|
|
|
|
|
|
|
|
import pmb.chroot
|
|
|
|
import pmb.chroot.apk
|
2017-06-09 17:22:25 +00:00
|
|
|
import pmb.chroot.other
|
|
|
|
import pmb.chroot.initfs
|
2017-05-26 20:08:45 +00:00
|
|
|
import pmb.config
|
|
|
|
import pmb.helpers.run
|
|
|
|
import pmb.install.blockdevice
|
2017-08-23 16:40:16 +00:00
|
|
|
import pmb.install.file
|
2017-08-24 21:07:36 +00:00
|
|
|
import pmb.install.recovery
|
2017-05-26 20:08:45 +00:00
|
|
|
import pmb.install
|
|
|
|
|
|
|
|
|
2017-08-24 21:07:36 +00:00
|
|
|
def mount_device_rootfs(args, suffix="native"):
|
|
|
|
"""
|
|
|
|
Mount the device rootfs.
|
|
|
|
"""
|
2017-05-26 20:08:45 +00:00
|
|
|
mountpoint = "/mnt/rootfs_" + args.device
|
|
|
|
pmb.helpers.mount.bind(args, args.work + "/chroot_rootfs_" + args.device,
|
2017-08-24 21:07:36 +00:00
|
|
|
args.work + "/chroot_" + suffix + mountpoint)
|
2017-08-03 16:01:00 +00:00
|
|
|
return mountpoint
|
|
|
|
|
|
|
|
|
2017-08-18 19:19:48 +00:00
|
|
|
def get_subpartitions_size(args):
|
|
|
|
"""
|
|
|
|
Calculate the size of the whole image and boot subpartition.
|
2017-08-03 16:01:00 +00:00
|
|
|
|
2017-08-18 19:19:48 +00:00
|
|
|
:returns: (full, boot) the size of the full image and boot
|
|
|
|
partition as integer in bytes
|
|
|
|
"""
|
|
|
|
# Calculate required sizes first
|
|
|
|
chroot = args.work + "/chroot_rootfs_" + args.device
|
|
|
|
root = pmb.helpers.other.folder_size(args, chroot)
|
|
|
|
boot = pmb.helpers.other.folder_size(args, chroot + "/boot")
|
|
|
|
home = pmb.helpers.other.folder_size(args, chroot + "/home")
|
2017-08-03 16:01:00 +00:00
|
|
|
|
2017-08-18 19:19:48 +00:00
|
|
|
# The home folder gets omitted when copying the rootfs to
|
|
|
|
# /dev/installp2
|
|
|
|
full = root - home
|
2017-08-03 16:01:00 +00:00
|
|
|
|
2017-08-18 19:19:48 +00:00
|
|
|
# Add some free space, see also:
|
|
|
|
# https://github.com/postmarketOS/pmbootstrap/pull/336
|
|
|
|
full *= 1.20
|
|
|
|
boot += 15 * 1024 * 1024
|
|
|
|
return (full, boot)
|
2017-08-03 16:01:00 +00:00
|
|
|
|
|
|
|
|
2017-08-18 19:19:48 +00:00
|
|
|
def copy_files_from_chroot(args):
|
|
|
|
"""
|
|
|
|
Copy all files from the rootfs chroot to /mnt/install, except
|
|
|
|
for the home folder (because /home will contain some empty
|
|
|
|
mountpoint folders).
|
|
|
|
"""
|
2017-08-03 16:01:00 +00:00
|
|
|
# Mount the device rootfs
|
2017-08-18 19:19:48 +00:00
|
|
|
logging.info("(native) copy rootfs_" + args.device + " to" +
|
|
|
|
" /mnt/install/")
|
2017-08-03 16:01:00 +00:00
|
|
|
mountpoint = mount_device_rootfs(args)
|
2017-08-18 19:19:48 +00:00
|
|
|
mountpoint_outside = args.work + "/chroot_native" + mountpoint
|
2017-05-26 20:08:45 +00:00
|
|
|
|
2017-08-18 19:19:48 +00:00
|
|
|
# Get all folders inside the device rootfs (except for home)
|
2017-05-26 20:08:45 +00:00
|
|
|
folders = []
|
2017-08-18 19:19:48 +00:00
|
|
|
for path in glob.glob(mountpoint_outside + "/*"):
|
|
|
|
if path.endswith("/home"):
|
|
|
|
continue
|
2017-05-26 20:08:45 +00:00
|
|
|
folders += [os.path.basename(path)]
|
|
|
|
|
|
|
|
# Run the copy command
|
|
|
|
pmb.chroot.root(args, ["cp", "-a"] + folders + ["/mnt/install/"],
|
|
|
|
working_dir=mountpoint)
|
|
|
|
|
|
|
|
|
2017-08-18 19:19:48 +00:00
|
|
|
def copy_files_other(args):
|
|
|
|
"""
|
2017-10-12 20:08:10 +00:00
|
|
|
Copy over keys, create /home/{user}.
|
2017-08-18 19:19:48 +00:00
|
|
|
"""
|
|
|
|
# Copy over keys
|
|
|
|
rootfs = args.work + "/chroot_native/mnt/install"
|
2017-05-26 20:08:45 +00:00
|
|
|
for key in glob.glob(args.work + "/config_apk_keys/*.pub"):
|
|
|
|
pmb.helpers.run.root(args, ["cp", key, rootfs + "/etc/apk/keys/"])
|
|
|
|
|
2017-10-12 20:08:10 +00:00
|
|
|
# Create /home/{user}
|
|
|
|
homedir = rootfs + "/home/" + args.user
|
2017-10-03 18:10:28 +00:00
|
|
|
pmb.helpers.run.root(args, ["mkdir", rootfs + "/home"])
|
2017-10-12 20:08:10 +00:00
|
|
|
pmb.helpers.run.root(args, ["cp", "-a", rootfs + "/etc/skel", homedir])
|
|
|
|
pmb.helpers.run.root(args, ["chown", "-R", "1000", homedir])
|
|
|
|
|
|
|
|
|
|
|
|
def set_user(args):
|
|
|
|
"""
|
|
|
|
Create user with UID 1000 if it doesn't exist
|
|
|
|
"""
|
|
|
|
suffix = "rootfs_" + args.device
|
|
|
|
if not pmb.chroot.user_exists(args, args.user, suffix):
|
|
|
|
pmb.chroot.root(args, ["adduser", "-D", "-u", "1000", args.user],
|
|
|
|
suffix)
|
|
|
|
pmb.chroot.root(args, ["addgroup", args.user, "wheel"], suffix)
|
2017-05-26 20:08:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
def set_user_password(args):
|
|
|
|
"""
|
|
|
|
Loop until the passwords for user and root have been changed successfully.
|
|
|
|
"""
|
2017-10-12 20:08:10 +00:00
|
|
|
logging.info(" *** SET LOGIN PASSWORD FOR: '" + args.user + "' ***")
|
2017-05-26 20:08:45 +00:00
|
|
|
suffix = "rootfs_" + args.device
|
|
|
|
while True:
|
|
|
|
try:
|
2017-10-12 20:08:10 +00:00
|
|
|
pmb.chroot.root(args, ["passwd", args.user], suffix, log=False)
|
2017-05-26 20:08:45 +00:00
|
|
|
break
|
|
|
|
except RuntimeError:
|
|
|
|
logging.info("WARNING: Failed to set the password. Try it"
|
|
|
|
" one more time.")
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2017-08-16 17:30:38 +00:00
|
|
|
def copy_ssh_key(args):
|
|
|
|
"""
|
2017-09-21 17:11:20 +00:00
|
|
|
Offer to copy user's SSH public keys to the device if they exist
|
2017-08-16 17:30:38 +00:00
|
|
|
"""
|
2017-09-21 17:11:20 +00:00
|
|
|
keys = []
|
|
|
|
for key in ["RSA", "Ed25519"]:
|
|
|
|
user_ssh_pubkey = os.path.expanduser("~/.ssh/id_" + key.lower() + ".pub")
|
|
|
|
if not os.path.exists(user_ssh_pubkey):
|
|
|
|
continue
|
|
|
|
if pmb.helpers.cli.confirm(
|
|
|
|
args, "Would you like to copy your " + key + " SSH public key to the device?"):
|
|
|
|
with open(user_ssh_pubkey, "r") as infile:
|
|
|
|
keys += infile.readlines()
|
|
|
|
|
|
|
|
if not len(keys):
|
2017-09-24 14:02:17 +00:00
|
|
|
logging.info("NOTE: Public SSH keys not found. Since no SSH keys " +
|
|
|
|
"were copied, you will need to use SSH password authentication!")
|
2017-09-21 17:11:20 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
authorized_keys = args.work + "/chroot_native/tmp/authorized_keys"
|
|
|
|
outfile = open(authorized_keys, "w")
|
|
|
|
for key in keys:
|
|
|
|
outfile.write("%s" % key)
|
|
|
|
outfile.close()
|
|
|
|
|
2017-10-12 20:08:10 +00:00
|
|
|
target = args.work + "/chroot_native/mnt/install/home/" + args.user + "/.ssh"
|
2017-09-21 17:11:20 +00:00
|
|
|
pmb.helpers.run.root(args, ["mkdir", target])
|
|
|
|
pmb.helpers.run.root(args, ["chmod", "700", target])
|
|
|
|
pmb.helpers.run.root(args, ["cp", authorized_keys, target + "/authorized_keys"])
|
|
|
|
pmb.helpers.run.root(args, ["rm", authorized_keys])
|
2017-10-12 20:08:10 +00:00
|
|
|
pmb.helpers.run.root(args, ["chown", "-R", "1000:1000", target])
|
2017-08-16 17:30:38 +00:00
|
|
|
|
|
|
|
|
2017-08-19 21:40:20 +00:00
|
|
|
def setup_keymap(args):
|
|
|
|
"""
|
|
|
|
Set the keymap with the setup-keymap utility if the device requires it
|
|
|
|
"""
|
|
|
|
suffix = "rootfs_" + args.device
|
|
|
|
info = pmb.parse.deviceinfo(args, device=args.device)
|
|
|
|
if "keymaps" not in info or info["keymaps"].strip() == "":
|
|
|
|
logging.info("NOTE: No valid keymap specified for device")
|
|
|
|
return
|
|
|
|
options = info["keymaps"].split(' ')
|
|
|
|
if (args.keymap != "" and
|
|
|
|
args.keymap is not None and
|
|
|
|
args.keymap in options):
|
|
|
|
layout, variant = args.keymap.split("/")
|
|
|
|
pmb.chroot.root(args, ["setup-keymap", layout, variant], suffix, log=False)
|
|
|
|
else:
|
|
|
|
logging.info("NOTE: No valid keymap specified for device")
|
|
|
|
|
|
|
|
|
2017-08-24 21:07:36 +00:00
|
|
|
def install_system_image(args):
|
2017-05-26 20:08:45 +00:00
|
|
|
# Partition and fill image/sdcard
|
|
|
|
logging.info("*** (3/5) PREPARE INSTALL BLOCKDEVICE ***")
|
|
|
|
pmb.chroot.shutdown(args, True)
|
2017-08-18 19:19:48 +00:00
|
|
|
(size_image, size_boot) = get_subpartitions_size(args)
|
2017-08-03 16:01:00 +00:00
|
|
|
pmb.install.blockdevice.create(args, size_image)
|
|
|
|
pmb.install.partition(args, size_boot)
|
2017-10-14 18:10:12 +00:00
|
|
|
if args.full_disk_encryption:
|
|
|
|
logging.info("WARNING: Full disk encryption is enabled!")
|
|
|
|
logging.info("Make sure that osk-sdl has been properly configured for your device")
|
|
|
|
logging.info("or else you will be unable to unlock the rootfs on boot!")
|
|
|
|
logging.info("If you started a device port, it is recommended you disable")
|
|
|
|
logging.info("FDE by re-running the install command with '--no-fde' until")
|
|
|
|
logging.info("you have properly configured osk-sdl. More information:")
|
|
|
|
logging.info("<https://postmarketos.org/osk-port>")
|
2017-05-26 20:08:45 +00:00
|
|
|
pmb.install.format(args)
|
|
|
|
|
|
|
|
# Just copy all the files
|
|
|
|
logging.info("*** (4/5) FILL INSTALL BLOCKDEVICE ***")
|
2017-08-18 19:19:48 +00:00
|
|
|
copy_files_from_chroot(args)
|
|
|
|
copy_files_other(args)
|
2017-08-16 17:30:38 +00:00
|
|
|
|
|
|
|
# If user has a ssh pubkey, offer to copy it to device
|
|
|
|
copy_ssh_key(args)
|
2017-05-26 20:08:45 +00:00
|
|
|
pmb.chroot.shutdown(args, True)
|
|
|
|
|
2017-08-02 16:21:50 +00:00
|
|
|
# Convert system image to sparse using img2simg
|
|
|
|
if args.deviceinfo["flash_sparse"] == "true":
|
|
|
|
logging.info("(native) make sparse system image")
|
|
|
|
pmb.chroot.apk.install(args, ["libsparse"])
|
|
|
|
sys_image = args.device + ".img"
|
|
|
|
sys_image_sparse = args.device + "-sparse.img"
|
|
|
|
pmb.chroot.user(args, ["img2simg", sys_image, sys_image_sparse],
|
2017-10-12 20:08:10 +00:00
|
|
|
working_dir="/home/pmos/rootfs/")
|
2017-08-02 16:21:50 +00:00
|
|
|
pmb.chroot.user(args, ["mv", "-f", sys_image_sparse, sys_image],
|
2017-10-12 20:08:10 +00:00
|
|
|
working_dir="/home/pmos/rootfs/")
|
2017-08-02 16:21:50 +00:00
|
|
|
|
2017-07-21 18:26:56 +00:00
|
|
|
# Kernel flash information
|
2017-05-26 20:08:45 +00:00
|
|
|
logging.info("*** (5/5) FLASHING TO DEVICE ***")
|
2017-07-21 18:26:56 +00:00
|
|
|
logging.info("Run the following to flash your installation to the"
|
|
|
|
" target device:")
|
|
|
|
logging.info("* pmbootstrap flasher flash_kernel")
|
|
|
|
logging.info(" Flashes the kernel + initramfs to your device:")
|
|
|
|
logging.info(" " + args.work + "/chroot_rootfs_" + args.device +
|
|
|
|
"/boot")
|
|
|
|
method = args.deviceinfo["flash_methods"]
|
|
|
|
if (method in pmb.config.flashers and "boot" in
|
|
|
|
pmb.config.flashers[method]["actions"]):
|
|
|
|
logging.info(" (NOTE: " + method + " also supports booting"
|
|
|
|
" the kernel/initramfs directly without flashing."
|
|
|
|
" Use 'pmbootstrap flasher boot' to do that.)")
|
|
|
|
|
|
|
|
# System flash information
|
|
|
|
if not args.sdcard:
|
|
|
|
logging.info("* pmbootstrap flasher flash_system")
|
|
|
|
logging.info(" Flashes the system image, that has been"
|
|
|
|
" generated to your device:")
|
2017-10-12 20:08:10 +00:00
|
|
|
logging.info(" " + args.work + "/chroot_native/home/pmos/rootfs/" +
|
2017-07-21 18:26:56 +00:00
|
|
|
args.device + ".img")
|
|
|
|
logging.info(" (NOTE: This file has a partition table,"
|
|
|
|
" which contains a boot- and root subpartition.)")
|
|
|
|
|
|
|
|
# Export information
|
|
|
|
logging.info("* If the above steps do not work, you can also create"
|
2017-09-06 20:14:03 +00:00
|
|
|
" symlinks to the generated files with 'pmbootstrap export'"
|
|
|
|
" and flash outside of pmbootstrap.")
|
2017-08-24 21:07:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
def install_recovery_zip(args):
|
|
|
|
logging.info("*** (3/4) CREATING RECOVERY-FLASHABLE ZIP ***")
|
|
|
|
suffix = "buildroot_" + args.deviceinfo["arch"]
|
|
|
|
mount_device_rootfs(args, suffix)
|
|
|
|
pmb.install.recovery.create_zip(args, suffix)
|
|
|
|
|
|
|
|
# Flash information
|
|
|
|
logging.info("*** (4/4) FLASHING TO DEVICE ***")
|
|
|
|
logging.info("Run the following to flash your installation to the"
|
|
|
|
" target device:")
|
|
|
|
logging.info("* pmbootstrap flasher --method adb sideload")
|
2017-09-30 20:01:20 +00:00
|
|
|
logging.info(" Flashes the installer zip to your device.")
|
2017-08-24 21:07:36 +00:00
|
|
|
|
|
|
|
# Export information
|
|
|
|
logging.info("* If this does not work, you can also create a"
|
2017-09-06 20:14:03 +00:00
|
|
|
" symlink to the generated zip with 'pmbootstrap"
|
|
|
|
" export' and flash outside of pmbootstrap.")
|
2017-08-24 21:07:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
def install(args):
|
|
|
|
# Number of steps for the different installation methods.
|
|
|
|
steps = 4 if args.android_recovery_zip else 5
|
|
|
|
|
|
|
|
# Install required programs in native chroot
|
|
|
|
logging.info("*** (1/{}) PREPARE NATIVE CHROOT ***".format(steps))
|
|
|
|
pmb.chroot.apk.install(args, pmb.config.install_native_packages,
|
|
|
|
build=False)
|
|
|
|
|
|
|
|
# List all packages to be installed (including the ones specified by --add)
|
|
|
|
# and upgrade the installed packages/apkindexes
|
|
|
|
logging.info('*** (2/{0}) CREATE DEVICE ROOTFS ("{1}") ***'.format(steps,
|
|
|
|
args.device))
|
|
|
|
install_packages = (pmb.config.install_device_packages +
|
|
|
|
["device-" + args.device])
|
|
|
|
if args.ui.lower() != "none":
|
|
|
|
install_packages += ["postmarketos-ui-" + args.ui]
|
|
|
|
suffix = "rootfs_" + args.device
|
|
|
|
pmb.chroot.apk.upgrade(args, suffix)
|
|
|
|
|
2017-10-12 20:08:10 +00:00
|
|
|
# Create final user and remove 'build' user
|
|
|
|
set_user(args)
|
|
|
|
|
2017-08-24 21:07:36 +00:00
|
|
|
# Explicitly call build on the install packages, to re-build them or any
|
|
|
|
# dependency, in case the version increased
|
|
|
|
if args.extra_packages.lower() != "none":
|
|
|
|
install_packages += args.extra_packages.split(",")
|
|
|
|
if args.add:
|
|
|
|
install_packages += args.add.split(",")
|
Close #453: Support mesa-dri-virtio in Qemu (#861)
The mesa driver, which ends up in the installation image, needs to be known
before the installation is done (in other words: when running the qemu action,
it is to late as the image has already been generated). That's why one can
choose the Qemu mesa driver in `pmbootstrap init` now:
```
Device [qemu-amd64]:
Which mesa driver do you prefer for your Qemu device? Only select something other
than the default if you are having graphical problems (such as glitches).
Mesa driver (dri-swrast/dri-virtio) [dri-virtio]:
```
It is still possible to select `dri-swrast`, because `dri-virtio` may not work
in all cases, and that way we could easily debug it or experiment with other
mesa drivers (e.g. the "vmware" one, which is supported by mesa and Qemu).
Other changes:
* `pmbootstrap qemu` accepts a `--display` variable now, which passes the value
directly to `qemu`'s `display` option. It defaults to `sdl,gl=on` (@PureTryOut
reported that to work best with plasma mobile on his PC). `--display` and
`--spice` (which is still working) are mutually exclusive.
* Removed obsolete telnet port pass-through: We only use the debug telnet port
since osk-sdl has been merged.
* Add show-cursor to the Qemu command line, so it shows a cursor in X11
* Refactored the spice code (`command_spice` only returns the spice command,
because it has all necessary information already) and the spice port can be
specified on the commandline now (previously it was hardcoded in one place and
then always looked up from there).
* Start comments with capital letters.
* Keep the log on the screen a bit shorter (e.g. Qemu command is written to the
"pmbootstrap log" anyway, so there's no need to display it again).
* linux-postmarketos-stable: Adjust kernel configs
x86_64, armhf: enable as modules:
CONFIG_DRM_VIRTIO_GPU, CONFIG_VIRTIO_PCI, CONFIG_VIRTIO_BALLOON
aarch64: all 3 options were already enabled as built-in (no change)
* Set '-vga virtio' for mesa-dri-virtio
2017-11-05 13:48:49 +00:00
|
|
|
if args.device.startswith("qemu-"):
|
|
|
|
install_packages += ["mesa-" + args.qemu_mesa_driver]
|
2017-08-24 21:07:36 +00:00
|
|
|
for pkgname in install_packages:
|
|
|
|
pmb.build.package(args, pkgname, args.deviceinfo["arch"])
|
|
|
|
|
|
|
|
# Install all packages to device rootfs chroot (and rebuild the initramfs,
|
|
|
|
# because that doesn't always happen automatically yet, e.g. when the user
|
|
|
|
# installed a hook without pmbootstrap - see #69 for more info)
|
|
|
|
pmb.chroot.apk.install(args, install_packages, suffix)
|
|
|
|
pmb.install.file.write_os_release(args, suffix)
|
|
|
|
for flavor in pmb.chroot.other.kernel_flavors_installed(args, suffix):
|
|
|
|
pmb.chroot.initfs.build(args, flavor, suffix)
|
|
|
|
|
|
|
|
# Set the user password
|
|
|
|
set_user_password(args)
|
|
|
|
|
|
|
|
# Set the keymap if the device requires it
|
|
|
|
setup_keymap(args)
|
|
|
|
|
2017-10-13 19:18:24 +00:00
|
|
|
# Set timezone
|
|
|
|
pmb.chroot.root(args, ["setup-timezone", "-z", args.timezone], suffix)
|
|
|
|
|
2017-08-24 21:07:36 +00:00
|
|
|
if args.android_recovery_zip:
|
|
|
|
install_recovery_zip(args)
|
|
|
|
else:
|
|
|
|
install_system_image(args)
|