1
0
Fork 1
mirror of https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git synced 2025-07-13 03:19:47 +03:00
pmbootstrap/pmbootstrap.py
Casey Connolly 8ca1718c7f add a tmpfs for scratch data
Some data like kernel mount overlays and the apk progress fifo don't
need to stick around, let's put them in a special new tmpfs which is
destroyed when pmbootstrap exits.

Signed-off-by: Casey Connolly <kcxt@postmarketos.org>
2025-06-13 21:47:42 +02:00

89 lines
3.2 KiB
Python
Executable file

#!/usr/bin/env python3
# -*- encoding: UTF-8 -*-
# Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later
# PYTHON_ARGCOMPLETE_OK
import sys
import pmb
import os
from pmb.init import sandbox
# Sanitise environment a bit
os.environ["SHELL"] = "/bin/sh" if os.path.exists("/bin/sh") else "/bin/bash"
if os.geteuid() == 0:
raise RuntimeError("pmbootstrap can't be run as root, please run it as a regular user.")
# FIXME: parse args before unshare(), this is really hacky
if "install" in sys.argv and "--disk" in sys.argv:
from pmb.init.run import run_root
disk = sys.argv[sys.argv.index("--disk")+1]
if not os.access(disk, os.W_OK):
cmd = ["sudo", "chown", str(os.getlogin()), disk]
if "-y" in sys.argv:
print(f"Using sudo to chown the target disk {disk}")
print(f" $ {' '.join(cmd)}")
run_root(cmd)
else:
print(f"The target disk '{disk}' is not writable by your user. Please run the following"
" command manually or press 'y' and enter your sudo password when prompted.")
print(f" $ {' '.join(cmd)}")
ans = input("> ")
if ans.lower() == "y":
run_root(cmd)
sandbox.acquire_privileges(become_root=False)
# Unshare mount and PID namespaces. We create a new PID namespace so
# that any log-running daemons (e.g. adbd, sccache) will be killed when
# pmbootstrap exits
sandbox.unshare(sandbox.CLONE_NEWNS | sandbox.CLONE_NEWPID)
# We are now PID 1 in a new PID namespace. We don't want to run all our
# logic as PID 1 since subprocess.Popen() seemingly causes our PID to
# change. So we fork now, the child process continues and we just wait
# around and propagate the exit code.
# This is all kinda hacky, we should integrate this with the acquire_privileges()
# implementation since it's already doing similar fork shenanigans, we could
# save a call to fork() this way. But for now it's fine.
pid = os.fork()
if pid > 0:
# We are PID 1! let's hang out
pid, wstatus = os.waitpid(pid, 0)
exitcode = os.waitstatus_to_exitcode(wstatus)
os._exit(exitcode)
# print("Caps: ")
# with open("/proc/self/status", "rb") as f:
# for line in f.readlines():
# if line.startswith(b"CapEff:"):
# print(line)
# print(f"cap_sys_admin: {sandbox.have_effective_cap(sandbox.CAP_SYS_ADMIN)}")
# print(f"single user: {sandbox.userns_has_single_user()}")
# We set up a very basic mount environment, where we just bind mount the host
# rootfs in. We can extend this in the future to isolate the pmb workdir but
# for now this is enough.
fsops = [
sandbox.BindOperation(
"/",
"/",
readonly=False,
required=True,
relative=False,
),
# Mount binfmt_misc at /tmp/pmb_binfmt_misc
sandbox.BinfmtOperation(pmb.config.binfmt_misc),
# /tmp/pmb is a tmpfs we can use for scratch data that will be removed
# on exit.
sandbox.TmpfsOperation("/tmp/pmb"),
]
sandbox.setup_mounts(fsops)
# Reset our CWD now that we're inside the mount namespace
os.chdir(os.environ["PWD"])
# A convenience wrapper for running pmbootstrap from the git repository. This
# script is not part of the python packaging, so don't add more logic here!
if __name__ == "__main__":
sys.exit(pmb.main())