mirror of
https://gitlab.com/kupfer/kupferbootstrap.git
synced 2025-02-22 21:25:43 -05:00
WIP: Add rootfs building and flashing and other misc commands
This commit is contained in:
parent
8886725971
commit
1f5357bca6
12 changed files with 410 additions and 11 deletions
|
@ -4,7 +4,8 @@ RUN pacman -Syu --noconfirm \
|
|||
python python-pip \
|
||||
devtools rsync \
|
||||
aarch64-linux-gnu-gcc aarch64-linux-gnu-binutils aarch64-linux-gnu-glibc aarch64-linux-gnu-linux-api-headers \
|
||||
git
|
||||
git \
|
||||
android-tools
|
||||
|
||||
RUN sed -i "s/EUID == 0/EUID == -1/g" $(which makepkg)
|
||||
|
||||
|
@ -18,7 +19,6 @@ RUN yes | pacman -Scc
|
|||
|
||||
RUN sed -i "s/SigLevel.*/SigLevel = Never/g" /etc/pacman.conf
|
||||
RUN sed -i "s|run_pacman |run_pacman --root \"/chroot/copy\" --arch aarch64 --config \"/app/src/pacman_copy.conf\" |g" $(which makepkg)
|
||||
RUN echo "Server = http://mirror.archlinuxarm.org/\$arch/\$repo" > /etc/pacman.d/aarch64_mirrorlist
|
||||
RUN mkdir -p /app/bin
|
||||
RUN ln -sf $(which aarch64-linux-gnu-strip) /app/bin/strip
|
||||
|
||||
|
|
54
boot.py
Normal file
54
boot.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
import os
|
||||
import urllib.request
|
||||
from image import get_device_and_flavour, get_image_name
|
||||
from logger import *
|
||||
import click
|
||||
import subprocess
|
||||
|
||||
|
||||
FASTBOOT = 'fastboot'
|
||||
|
||||
JUMPDRIVE = 'jumpdrive'
|
||||
jumpdrive_version = '0.8'
|
||||
|
||||
boot_strategies = {
|
||||
'oneplus-enchilada': FASTBOOT,
|
||||
'xiaomi-beryllium-ebbg': FASTBOOT,
|
||||
'xiaomi-beryllium-tianma': FASTBOOT,
|
||||
}
|
||||
|
||||
|
||||
def dump_bootimg(image_name: str) -> str:
|
||||
path = '/tmp/boot.img'
|
||||
result = subprocess.run(['debugfs',
|
||||
image_name,
|
||||
'-R', f'dump /boot/boot.img {path}'])
|
||||
if result.returncode != 0:
|
||||
logging.fatal(f'Faild to dump boot.img')
|
||||
exit(1)
|
||||
return path
|
||||
|
||||
|
||||
@click.command(name='boot', help=f'Leave TYPE empty or choose \'{JUMPDRIVE}\'')
|
||||
@verbose_option
|
||||
@click.argument('type', required=False)
|
||||
def cmd_boot(verbose, type):
|
||||
setup_logging(verbose)
|
||||
|
||||
device, flavour = get_device_and_flavour()
|
||||
image_name = get_image_name(device, flavour)
|
||||
strategy = boot_strategies[device]
|
||||
|
||||
if strategy == FASTBOOT:
|
||||
if type == JUMPDRIVE:
|
||||
file = f'boot-{device}.img'
|
||||
path = os.path.join('/var/cache/jumpdrive', file)
|
||||
urllib.request.urlretrieve(
|
||||
f'https://github.com/dreemurrs-embedded/Jumpdrive/releases/download/{jumpdrive_version}/{file}', path)
|
||||
else:
|
||||
path = dump_bootimg(image_name)
|
||||
|
||||
result = subprocess.run(['fastboot', 'boot', path])
|
||||
if result.returncode != 0:
|
||||
logging.fatal(f'Failed to boot {path} using fastboot')
|
||||
exit(1)
|
2
cache.py
2
cache.py
|
@ -14,7 +14,7 @@ def cmd_cache():
|
|||
def cmd_clean(verbose):
|
||||
setup_logging(verbose)
|
||||
|
||||
for dir in ['/chroot', '/var/cache/pacman/pkg']:
|
||||
for dir in ['/chroot', '/var/cache/pacman/pkg', '/var/cache/jumpdrive']:
|
||||
for file in os.listdir(dir):
|
||||
path = os.path.join(dir, file)
|
||||
if os.path.isdir(path):
|
||||
|
|
81
flash.py
Normal file
81
flash.py
Normal file
|
@ -0,0 +1,81 @@
|
|||
from image import get_device_and_flavour, get_image_name
|
||||
from boot import dump_bootimg
|
||||
import os
|
||||
import subprocess
|
||||
import click
|
||||
from logger import *
|
||||
|
||||
ROOTFS = 'rootfs'
|
||||
BOOTIMG = 'bootimg'
|
||||
|
||||
EMMC = 'emmc'
|
||||
EMMCFILE = 'emmc-file'
|
||||
MICROSD = 'microsd'
|
||||
locations = [EMMC, EMMCFILE, MICROSD]
|
||||
|
||||
|
||||
@click.command(name='flash')
|
||||
@verbose_option
|
||||
@click.argument('what')
|
||||
@click.argument('location', required=False)
|
||||
def cmd_flash(verbose, what, location):
|
||||
setup_logging(verbose)
|
||||
|
||||
device, flavour = get_device_and_flavour()
|
||||
image_name = get_image_name(device, flavour)
|
||||
|
||||
if what == ROOTFS:
|
||||
if location == None:
|
||||
logging.info(f'You need to specify a location to flash {what} to')
|
||||
exit(1)
|
||||
if location not in locations:
|
||||
logging.info(
|
||||
f'Invalid location {location}. Choose one of {", ".join(locations)} for location')
|
||||
exit(1)
|
||||
|
||||
dir = '/dev/disk/by-id'
|
||||
for file in os.listdir(dir):
|
||||
sanitized_file = file.replace('-', '').replace('_', '').lower()
|
||||
if f'jumpdrive{location.split("-")[0]}' in sanitized_file:
|
||||
path = os.path.realpath(os.path.join(dir, file))
|
||||
result = subprocess.run(['lsblk',
|
||||
path,
|
||||
'-o', 'SIZE'],
|
||||
capture_output=True)
|
||||
if result.returncode != 0:
|
||||
logging.info(f'Failed to lsblk {path}')
|
||||
exit(1)
|
||||
if result.stdout == b'SIZE\n 0B\n':
|
||||
logging.info(
|
||||
f'Disk {path} has a size of 0B. That probably means it is not available (e.g. no microSD inserted or no microSD card slot installed in the device) or corrupt or defect')
|
||||
exit(1)
|
||||
|
||||
if location.endswith('-file'):
|
||||
logging.fatal('Not implemented yet')
|
||||
exit()
|
||||
else:
|
||||
result = subprocess.run(['dd',
|
||||
f'if={image_name}',
|
||||
f'of={path}',
|
||||
'bs=20M',
|
||||
'iflag=direct',
|
||||
'oflag=direct',
|
||||
'status=progress'])
|
||||
if result.returncode != 0:
|
||||
logging.info(f'Failed to flash {image_name} to {path}')
|
||||
exit(1)
|
||||
|
||||
elif what == BOOTIMG:
|
||||
result = subprocess.run(['fastboot', 'erase', 'dtbo'])
|
||||
if result.returncode != 0:
|
||||
logging.info(f'Failed to erase dtbo')
|
||||
exit(1)
|
||||
|
||||
path = dump_bootimg(image_name)
|
||||
result = subprocess.run(['fastboot', 'flash', 'boot', path])
|
||||
if result.returncode != 0:
|
||||
logging.info(f'Failed to flash boot.img')
|
||||
exit(1)
|
||||
else:
|
||||
logging.fatal(f'Unknown what {what}')
|
||||
exit(1)
|
38
forwarding.py
Normal file
38
forwarding.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
import click
|
||||
import subprocess
|
||||
from logger import *
|
||||
|
||||
|
||||
@click.command(name='forwarding')
|
||||
@verbose_option
|
||||
def cmd_forwarding(verbose):
|
||||
setup_logging(verbose)
|
||||
|
||||
result = subprocess.run(['sysctl', 'net.ipv4.ip_forward=1'])
|
||||
if result.returncode != 0:
|
||||
logging.fatal(f'Failed to enable ipv4 forward via sysctl')
|
||||
exit(1)
|
||||
|
||||
result = subprocess.run(['iptables', '-P', 'FORWARD', 'ACCEPT'])
|
||||
if result.returncode != 0:
|
||||
logging.fatal(f'Failed set iptables rule')
|
||||
exit(1)
|
||||
|
||||
result = subprocess.run(['iptables',
|
||||
'-A', 'POSTROUTING',
|
||||
'-t', 'nat',
|
||||
'-j', 'MASQUERADE',
|
||||
'-s', '172.16.42.0/24'])
|
||||
if result.returncode != 0:
|
||||
logging.fatal(f'Failed set iptables rule')
|
||||
exit(1)
|
||||
|
||||
result = subprocess.run(['ssh',
|
||||
'-o', 'GlobalKnownHostsFile=/dev/null',
|
||||
'-o', 'UserKnownHostsFile=/dev/null',
|
||||
'-o', 'StrictHostKeyChecking=no',
|
||||
'-t', 'kupfer@172.16.42.1',
|
||||
'sudo route add default gw 172.16.42.2'])
|
||||
if result.returncode != 0:
|
||||
logging.fatal(f'Failed to add gateway over ssh')
|
||||
exit(1)
|
198
image.py
Normal file
198
image.py
Normal file
|
@ -0,0 +1,198 @@
|
|||
import atexit
|
||||
from logging import root
|
||||
import os
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import time
|
||||
import click
|
||||
from logger import *
|
||||
|
||||
devices = {
|
||||
'oneplus-enchilada': ['sdm845-oneplus-enchilada'],
|
||||
'xiaomi-beryllium-ebbg': ['sdm845-xiaomi-beryllium-ebbg'],
|
||||
'xiaomi-beryllium-tianma': ['sdm845-xiaomi-beryllium-tianma'],
|
||||
}
|
||||
|
||||
flavours = {
|
||||
'barebone': [],
|
||||
'phosh': [],
|
||||
'plasma-mobile': [],
|
||||
}
|
||||
|
||||
|
||||
def get_device_and_flavour() -> tuple[str, str]:
|
||||
if not os.path.exists('.device'):
|
||||
logging.fatal(
|
||||
f'Please set the device using \'kupferbootstrap image device ...\'')
|
||||
exit(1)
|
||||
if not os.path.exists('.flavour'):
|
||||
logging.fatal(
|
||||
f'Please set the flavour using \'kupferbootstrap image flavour ...\'')
|
||||
exit(1)
|
||||
|
||||
with open('.device', 'r') as file:
|
||||
device = file.read()
|
||||
with open('.flavour', 'r') as file:
|
||||
flavour = file.read()
|
||||
|
||||
return (device, flavour)
|
||||
|
||||
|
||||
def get_image_name(device, flavour):
|
||||
return f'{device}-{flavour}-rootfs.img'
|
||||
|
||||
|
||||
def mount_rootfs_image(path):
|
||||
rootfs_mount = '/mnt/kupfer/rootfs'
|
||||
if not os.path.exists(rootfs_mount):
|
||||
os.makedirs(rootfs_mount)
|
||||
|
||||
def umount():
|
||||
subprocess.run(['umount', '-lc', rootfs_mount],
|
||||
stderr=subprocess.DEVNULL)
|
||||
atexit.register(umount)
|
||||
|
||||
result = subprocess.run(['mount',
|
||||
'-o', 'loop',
|
||||
path,
|
||||
rootfs_mount])
|
||||
if result.returncode != 0:
|
||||
logging.fatal(f'Failed to loop mount {path} to {rootfs_mount}')
|
||||
exit(1)
|
||||
|
||||
return rootfs_mount
|
||||
|
||||
|
||||
@click.group(name='image')
|
||||
def cmd_image():
|
||||
pass
|
||||
|
||||
|
||||
@click.command(name='device')
|
||||
@verbose_option
|
||||
@click.argument('device')
|
||||
def cmd_device(verbose, device):
|
||||
setup_logging(verbose)
|
||||
|
||||
for key in devices.keys():
|
||||
if '-'.join(key.split('-')[1:]) == device:
|
||||
device = key
|
||||
break
|
||||
|
||||
if device not in devices:
|
||||
logging.fatal(
|
||||
f'Unknown device {device}. Pick one from:\n{", ".join(devices.keys())}')
|
||||
exit(1)
|
||||
|
||||
logging.info(f'Setting device to {device}')
|
||||
|
||||
with open('.device', 'w') as file:
|
||||
file.write(device)
|
||||
|
||||
|
||||
@click.command(name='flavour')
|
||||
@verbose_option
|
||||
@click.argument('flavour')
|
||||
def cmd_flavour(verbose, flavour):
|
||||
setup_logging(verbose)
|
||||
|
||||
if flavour not in flavours:
|
||||
logging.fatal(
|
||||
f'Unknown flavour {flavour}. Pick one from:\n{", ".join(flavours.keys())}')
|
||||
exit(1)
|
||||
|
||||
logging.info(f'Setting flavour to {flavour}')
|
||||
|
||||
with open('.flavour', 'w') as file:
|
||||
file.write(flavour)
|
||||
|
||||
|
||||
@click.command(name='build')
|
||||
@verbose_option
|
||||
def cmd_build(verbose):
|
||||
setup_logging(verbose)
|
||||
|
||||
device, flavour = get_device_and_flavour()
|
||||
image_name = get_image_name(device, flavour)
|
||||
|
||||
shutil.copyfile('/app/src/pacman.conf', '/app/src/pacman_copy.conf')
|
||||
with open('/app/src/pacman_copy.conf', 'a') as file:
|
||||
file.write(
|
||||
'\n\n[main]\nServer = https://gitlab.com/kupfer/packages/prebuilts/-/raw/main/$repo')
|
||||
file.write(
|
||||
'\n\n[device]\nServer = https://gitlab.com/kupfer/packages/prebuilts/-/raw/main/$repo')
|
||||
|
||||
if not os.path.exists(image_name):
|
||||
result = subprocess.run(['fallocate',
|
||||
'-l', '4G',
|
||||
image_name])
|
||||
if result.returncode != 0:
|
||||
logging.fatal(f'Failed to allocate {image_name}')
|
||||
exit(1)
|
||||
|
||||
result = subprocess.run(['mkfs.ext4',
|
||||
'-L', 'kupfer',
|
||||
image_name])
|
||||
if result.returncode != 0:
|
||||
logging.fatal(f'Failed to create ext4 filesystem on {image_name}')
|
||||
exit(1)
|
||||
|
||||
rootfs_mount = mount_rootfs_image(image_name)
|
||||
|
||||
result = subprocess.run(['pacstrap',
|
||||
'-C', '/app/src/pacman_copy.conf',
|
||||
'-c',
|
||||
'-G',
|
||||
rootfs_mount,
|
||||
'base', 'base-kupfer']
|
||||
+ devices[device]
|
||||
+ flavours[flavour]
|
||||
+ ['--needed', '--overwrite=*', '-yyuu'])
|
||||
|
||||
with open(os.path.join(rootfs_mount, 'install'), 'w') as file:
|
||||
user = 'kupfer'
|
||||
password = '123456'
|
||||
groups = ['network', 'video', 'audio', 'optical', 'storage',
|
||||
'input', 'scanner', 'games', 'lp', 'rfkill', 'wheel']
|
||||
file.write('\n'.join([
|
||||
f'if ! id -u "{user}" >/dev/null 2>&1; then',
|
||||
f' useradd -m {user}',
|
||||
f'fi',
|
||||
f'usermod -a -G {",".join(groups)} {user}',
|
||||
f'echo "{user}:{password}" | chpasswd',
|
||||
f'chown {user}:{user} /home/{user} -R',
|
||||
]))
|
||||
result = subprocess.run(['arch-chroot',
|
||||
rootfs_mount,
|
||||
'/bin/bash', '/install'])
|
||||
os.unlink(os.path.join(rootfs_mount, 'install'))
|
||||
if result.returncode != 0:
|
||||
logging.fatal('Failed to setup user')
|
||||
exit(1)
|
||||
|
||||
|
||||
"""
|
||||
This doesn't work, because the mount isn't passed through to the real host
|
||||
"""
|
||||
|
||||
"""
|
||||
@click.command(name='inspect')
|
||||
@verbose_option
|
||||
def cmd_inspect(verbose):
|
||||
setup_logging(verbose)
|
||||
|
||||
device, flavour = get_device_and_flavour()
|
||||
image_name = get_image_name(device, flavour)
|
||||
|
||||
rootfs_mount = mount_rootfs_image(image_name)
|
||||
|
||||
logging.info(f'Inspect the rootfs image at {rootfs_mount}')
|
||||
|
||||
signal.pause()
|
||||
"""
|
||||
|
||||
cmd_image.add_command(cmd_device)
|
||||
cmd_image.add_command(cmd_flavour)
|
||||
cmd_image.add_command(cmd_build)
|
||||
# cmd_image.add_command(cmd_inspect)
|
10
main.py
10
main.py
|
@ -1,5 +1,10 @@
|
|||
from packages import cmd_packages
|
||||
from cache import cmd_cache
|
||||
from image import cmd_image
|
||||
from boot import cmd_boot
|
||||
from flash import cmd_flash
|
||||
from ssh import cmd_ssh
|
||||
from forwarding import cmd_forwarding
|
||||
import click
|
||||
|
||||
|
||||
|
@ -10,3 +15,8 @@ def cli():
|
|||
|
||||
cli.add_command(cmd_cache)
|
||||
cli.add_command(cmd_packages)
|
||||
cli.add_command(cmd_image)
|
||||
cli.add_command(cmd_boot)
|
||||
cli.add_command(cmd_flash)
|
||||
cli.add_command(cmd_ssh)
|
||||
cli.add_command(cmd_forwarding)
|
||||
|
|
|
@ -123,8 +123,6 @@ def setup_chroot():
|
|||
file.write('\n\n[device]\nServer = file:///src/prebuilts/device')
|
||||
shutil.copyfile('/app/src/pacman_copy.conf',
|
||||
'/chroot/root/etc/pacman.conf')
|
||||
shutil.copyfile('/etc/pacman.d/aarch64_mirrorlist',
|
||||
'/chroot/root/etc/pacman.d/aarch64_mirrorlist')
|
||||
result = subprocess.run(pacman_cmd +
|
||||
['-Sy',
|
||||
'--root', '/chroot/root',
|
||||
|
|
|
@ -70,19 +70,19 @@ LocalFileSigLevel = Never
|
|||
# after the header, and they will be used before the default mirrors.
|
||||
|
||||
[core]
|
||||
Include = /etc/pacman.d/aarch64_mirrorlist
|
||||
Server = http://mirror.archlinuxarm.org/$arch/$repo
|
||||
|
||||
[extra]
|
||||
Include = /etc/pacman.d/aarch64_mirrorlist
|
||||
Server = http://mirror.archlinuxarm.org/$arch/$repo
|
||||
|
||||
[community]
|
||||
Include = /etc/pacman.d/aarch64_mirrorlist
|
||||
Server = http://mirror.archlinuxarm.org/$arch/$repo
|
||||
|
||||
[alarm]
|
||||
Include = /etc/pacman.d/aarch64_mirrorlist
|
||||
Server = http://mirror.archlinuxarm.org/$arch/$repo
|
||||
|
||||
[aur]
|
||||
Include = /etc/pacman.d/aarch64_mirrorlist
|
||||
Server = http://mirror.archlinuxarm.org/$arch/$repo
|
||||
|
||||
# An example of a custom package repository. See the pacman manpage for
|
||||
# tips on creating your own repositories.
|
||||
|
|
|
@ -28,4 +28,4 @@ sed -i "s/@CARCH@/aarch64/g" pacman.conf
|
|||
sed -i "s/#ParallelDownloads.*/ParallelDownloads = 8/g" pacman.conf
|
||||
sed -i "s/SigLevel.*/SigLevel = Never/g" pacman.conf
|
||||
sed -i "s/^CheckSpace/#CheckSpace/g" pacman.conf
|
||||
sed -i "s|/mirrorlist|/aarch64_mirrorlist|g" pacman.conf
|
||||
sed -i "s|Include = /etc/pacman.d/mirrorlist|Server = http://mirror.archlinuxarm.org/\$arch/\$repo|g" pacman.conf
|
||||
|
|
15
ssh.py
Normal file
15
ssh.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
import subprocess
|
||||
import click
|
||||
from logger import *
|
||||
|
||||
|
||||
@click.command(name='ssh')
|
||||
@verbose_option
|
||||
def cmd_ssh(verbose):
|
||||
setup_logging(verbose)
|
||||
|
||||
subprocess.run(['ssh',
|
||||
'-o', 'GlobalKnownHostsFile=/dev/null',
|
||||
'-o', 'UserKnownHostsFile=/dev/null',
|
||||
'-o', 'StrictHostKeyChecking=no',
|
||||
'kupfer@172.16.42.1'])
|
|
@ -43,10 +43,15 @@ else:
|
|||
'run',
|
||||
'--name', 'kupferbootstrap',
|
||||
'--rm',
|
||||
'--interactive',
|
||||
'--tty',
|
||||
'--privileged',
|
||||
'-v', f'{os.getcwd()}:/src:z',
|
||||
'-v', f'{os.path.join(appdirs.user_cache_dir("kupfer"),"chroot")}:/chroot:z',
|
||||
'-v', f'{os.path.join(appdirs.user_cache_dir("kupfer"),"pacman")}:/var/cache/pacman/pkg:z',
|
||||
'-v', f'{os.path.join(appdirs.user_cache_dir("kupfer"),"jumpdrive")}:/var/cache/jumpdrive:z',
|
||||
'-v', '/dev:/dev',
|
||||
#'-v', '/mnt/kupfer:/mnt/kupfer:z',
|
||||
'-v', '/usr/share/i18n/locales:/usr/share/i18n/locales:ro'] +
|
||||
[tag,
|
||||
'kupferbootstrap']
|
||||
|
|
Loading…
Add table
Reference in a new issue