forked from Mirror/pmbootstrap
chroot: run: support running multiple commands with one call (MR 2252)
Building the command strings and entering the chroot is a not-insubstantial amount of overhead. Implement support for running multiple commands with a new pmb.chroot.rootm() function. TODO: add alternative for chroot.user and run.root/user. Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
This commit is contained in:
parent
3a74018f89
commit
8a61d67053
5 changed files with 26 additions and 17 deletions
|
@ -29,11 +29,11 @@ def executables_absolute_path():
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def root(cmd: Sequence[PathString], chroot: Chroot=Chroot.native(), working_dir: PurePath=PurePath("/"), output="log",
|
def rootm(cmds: Sequence[Sequence[PathString]], chroot: Chroot=Chroot.native(), working_dir: PurePath=PurePath("/"), output="log",
|
||||||
output_return=False, check=None, env={},
|
output_return=False, check=None, env={},
|
||||||
disable_timeout=False, add_proxy_env_vars=True):
|
disable_timeout=False, add_proxy_env_vars=True):
|
||||||
"""
|
"""
|
||||||
Run a command inside a chroot as root.
|
Run a list of commands inside a chroot as root.
|
||||||
|
|
||||||
:param env: dict of environment variables to be passed to the command, e.g.
|
:param env: dict of environment variables to be passed to the command, e.g.
|
||||||
{"JOBS": "5"}
|
{"JOBS": "5"}
|
||||||
|
@ -48,7 +48,7 @@ def root(cmd: Sequence[PathString], chroot: Chroot=Chroot.native(), working_dir:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Convert any Path objects to their string representation
|
# Convert any Path objects to their string representation
|
||||||
cmd_str = [os.fspath(x) for x in cmd]
|
cmd_strs = [[os.fspath(x) for x in cmd] for cmd in cmds]
|
||||||
|
|
||||||
# Readable log message (without all the escaping)
|
# Readable log message (without all the escaping)
|
||||||
msg = f"({chroot}) % "
|
msg = f"({chroot}) % "
|
||||||
|
@ -56,7 +56,7 @@ def root(cmd: Sequence[PathString], chroot: Chroot=Chroot.native(), working_dir:
|
||||||
msg += f"{key}={value} "
|
msg += f"{key}={value} "
|
||||||
if working_dir != PurePath("/"):
|
if working_dir != PurePath("/"):
|
||||||
msg += f"cd {working_dir}; "
|
msg += f"cd {working_dir}; "
|
||||||
msg += " ".join(cmd_str)
|
msg += "; ".join([" ".join(cmd_str) for cmd_str in cmd_strs])
|
||||||
|
|
||||||
# Merge env with defaults into env_all
|
# Merge env with defaults into env_all
|
||||||
env_all: Env = {"CHARSET": "UTF-8",
|
env_all: Env = {"CHARSET": "UTF-8",
|
||||||
|
@ -78,16 +78,23 @@ def root(cmd: Sequence[PathString], chroot: Chroot=Chroot.native(), working_dir:
|
||||||
# cmd_sudo: ["sudo", "env", "-i", "sh", "-c", "PATH=... /sbin/chroot ..."]
|
# cmd_sudo: ["sudo", "env", "-i", "sh", "-c", "PATH=... /sbin/chroot ..."]
|
||||||
executables = executables_absolute_path()
|
executables = executables_absolute_path()
|
||||||
cmd_chroot = [executables["chroot"], chroot.path, "/bin/sh", "-c",
|
cmd_chroot = [executables["chroot"], chroot.path, "/bin/sh", "-c",
|
||||||
pmb.helpers.run_core.flat_cmd(cmd_str, Path(working_dir))]
|
pmb.helpers.run_core.flat_cmd(cmd_strs, Path(working_dir))]
|
||||||
cmd_sudo = pmb.config.sudo([
|
cmd_sudo = pmb.config.sudo([
|
||||||
"env", "-i", executables["sh"], "-c",
|
"env", "-i", executables["sh"], "-c",
|
||||||
pmb.helpers.run_core.flat_cmd(cmd_chroot, env=env_all)]
|
pmb.helpers.run_core.flat_cmd([cmd_chroot], env=env_all)]
|
||||||
)
|
)
|
||||||
return pmb.helpers.run_core.core(msg, cmd_sudo, None, output,
|
return pmb.helpers.run_core.core(msg, cmd_sudo, None, output,
|
||||||
output_return, check, True,
|
output_return, check, True,
|
||||||
disable_timeout)
|
disable_timeout)
|
||||||
|
|
||||||
|
|
||||||
|
def root(cmds: Sequence[PathString], chroot: Chroot=Chroot.native(), working_dir: PurePath=PurePath("/"), output="log",
|
||||||
|
output_return=False, check=None, env={},
|
||||||
|
disable_timeout=False, add_proxy_env_vars=True):
|
||||||
|
return rootm([cmds], chroot, working_dir, output, output_return, check, env,
|
||||||
|
disable_timeout, add_proxy_env_vars)
|
||||||
|
|
||||||
|
|
||||||
def user(cmd, chroot: Chroot=Chroot.native(), working_dir: Path = Path("/"), output="log",
|
def user(cmd, chroot: Chroot=Chroot.native(), working_dir: Path = Path("/"), output="log",
|
||||||
output_return=False, check=None, env={}):
|
output_return=False, check=None, env={}):
|
||||||
"""
|
"""
|
||||||
|
@ -107,7 +114,7 @@ def user(cmd, chroot: Chroot=Chroot.native(), working_dir: Path = Path("/"), out
|
||||||
if "HOME" not in env:
|
if "HOME" not in env:
|
||||||
env["HOME"] = "/home/pmos"
|
env["HOME"] = "/home/pmos"
|
||||||
|
|
||||||
flat_cmd = pmb.helpers.run_core.flat_cmd(cmd, env=env)
|
flat_cmd = pmb.helpers.run_core.flat_cmd([cmd], env=env)
|
||||||
cmd = ["busybox", "su", "pmos", "-c", flat_cmd]
|
cmd = ["busybox", "su", "pmos", "-c", flat_cmd]
|
||||||
return pmb.chroot.root(cmd, chroot, working_dir, output,
|
return pmb.chroot.root(cmd, chroot, working_dir, output,
|
||||||
output_return, check, {},
|
output_return, check, {},
|
||||||
|
|
|
@ -41,7 +41,7 @@ def _create_command_with_progress(command, fifo):
|
||||||
"""
|
"""
|
||||||
flags = ["--no-progress", "--progress-fd", "3"]
|
flags = ["--no-progress", "--progress-fd", "3"]
|
||||||
command_full = [command[0]] + flags + command[1:]
|
command_full = [command[0]] + flags + command[1:]
|
||||||
command_flat = pmb.helpers.run_core.flat_cmd(command_full)
|
command_flat = pmb.helpers.run_core.flat_cmd([command_full])
|
||||||
command_flat = f"exec 3>{fifo}; {command_flat}"
|
command_flat = f"exec 3>{fifo}; {command_flat}"
|
||||||
return ["sh", "-c", command_flat]
|
return ["sh", "-c", command_flat]
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ def user(cmd: Sequence[PathString], working_dir: Optional[Path] = None, output:
|
||||||
env = env.copy()
|
env = env.copy()
|
||||||
pmb.helpers.run_core.add_proxy_env_vars(env)
|
pmb.helpers.run_core.add_proxy_env_vars(env)
|
||||||
if env:
|
if env:
|
||||||
cmd_parts = ["sh", "-c", pmb.helpers.run_core.flat_cmd(cmd_parts, env=env)]
|
cmd_parts = ["sh", "-c", pmb.helpers.run_core.flat_cmd([cmd_parts], env=env)]
|
||||||
return pmb.helpers.run_core.core(msg, cmd_parts, working_dir, output,
|
return pmb.helpers.run_core.core(msg, cmd_parts, working_dir, output,
|
||||||
output_return, check, sudo)
|
output_return, check, sudo)
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ def root(cmd: Sequence[PathString], working_dir=None, output="log", output_retur
|
||||||
pmb.helpers.run_core.add_proxy_env_vars(env)
|
pmb.helpers.run_core.add_proxy_env_vars(env)
|
||||||
|
|
||||||
if env:
|
if env:
|
||||||
cmd = ["sh", "-c", pmb.helpers.run_core.flat_cmd(cmd, env=env)]
|
cmd = ["sh", "-c", pmb.helpers.run_core.flat_cmd([cmd], env=env)]
|
||||||
cmd = pmb.config.sudo(cmd)
|
cmd = pmb.config.sudo(cmd)
|
||||||
|
|
||||||
return user(cmd, working_dir, output, output_return, check, env,
|
return user(cmd, working_dir, output, output_return, check, env,
|
||||||
|
|
|
@ -20,10 +20,10 @@ import pmb.helpers.run
|
||||||
called by core(). """
|
called by core(). """
|
||||||
|
|
||||||
|
|
||||||
def flat_cmd(cmd: Sequence[PathString], working_dir: Optional[Path]=None, env: Env={}):
|
def flat_cmd(cmds: Sequence[Sequence[PathString]], working_dir: Optional[Path]=None, env: Env={}):
|
||||||
"""Convert a shell command passed as list into a flat shell string with proper escaping.
|
"""Convert a shell command passed as list into a flat shell string with proper escaping.
|
||||||
|
|
||||||
:param cmd: command as list, e.g. ["echo", "string with spaces"]
|
:param cmds: list of commands as list, e.g. ["echo", "string with spaces"]
|
||||||
:param working_dir: when set, prepend "cd ...;" to execute the command
|
:param working_dir: when set, prepend "cd ...;" to execute the command
|
||||||
in the given working directory
|
in the given working directory
|
||||||
:param env: dict of environment variables to be passed to the command, e.g.
|
:param env: dict of environment variables to be passed to the command, e.g.
|
||||||
|
@ -36,8 +36,10 @@ def flat_cmd(cmd: Sequence[PathString], working_dir: Optional[Path]=None, env: E
|
||||||
escaped = []
|
escaped = []
|
||||||
for key, value in env.items():
|
for key, value in env.items():
|
||||||
escaped.append(key + "=" + shlex.quote(os.fspath(value)))
|
escaped.append(key + "=" + shlex.quote(os.fspath(value)))
|
||||||
for i in range(len(cmd)):
|
for cmd in cmds:
|
||||||
escaped.append(shlex.quote(os.fspath(cmd[i])))
|
for i in range(len(cmd)):
|
||||||
|
escaped.append(shlex.quote(os.fspath(cmd[i])))
|
||||||
|
escaped.append(";")
|
||||||
|
|
||||||
# Prepend working dir
|
# Prepend working dir
|
||||||
ret = " ".join(escaped)
|
ret = " ".join(escaped)
|
||||||
|
|
|
@ -34,7 +34,7 @@ def scp_abuild_key(args: PmbArgs, user: str, host: str, port: str):
|
||||||
keyname = os.path.join("/tmp", os.path.basename(key))
|
keyname = os.path.join("/tmp", os.path.basename(key))
|
||||||
remote_cmd_l: List[PathString] = ['sudo', '-p', pmb.config.sideload_sudo_prompt,
|
remote_cmd_l: List[PathString] = ['sudo', '-p', pmb.config.sideload_sudo_prompt,
|
||||||
'-S', 'mv', '-n', keyname, "/etc/apk/keys/"]
|
'-S', 'mv', '-n', keyname, "/etc/apk/keys/"]
|
||||||
remote_cmd = pmb.helpers.run_core.flat_cmd(remote_cmd_l)
|
remote_cmd = pmb.helpers.run_core.flat_cmd([remote_cmd_l])
|
||||||
command = ['ssh', '-t', '-p', port, f'{user}@{host}', remote_cmd]
|
command = ['ssh', '-t', '-p', port, f'{user}@{host}', remote_cmd]
|
||||||
pmb.helpers.run.user(command, output="tui")
|
pmb.helpers.run.user(command, output="tui")
|
||||||
|
|
||||||
|
@ -72,8 +72,8 @@ def ssh_install_apks(args: PmbArgs, user, host, port, paths):
|
||||||
logging.info(f"Installing packages at {user}@{host}")
|
logging.info(f"Installing packages at {user}@{host}")
|
||||||
add_cmd = ['sudo', '-p', pmb.config.sideload_sudo_prompt,
|
add_cmd = ['sudo', '-p', pmb.config.sideload_sudo_prompt,
|
||||||
'-S', 'apk', '--wait', '30', 'add'] + remote_paths
|
'-S', 'apk', '--wait', '30', 'add'] + remote_paths
|
||||||
add_cmd = pmb.helpers.run_core.flat_cmd(add_cmd)
|
add_cmd = pmb.helpers.run_core.flat_cmd([add_cmd])
|
||||||
clean_cmd = pmb.helpers.run_core.flat_cmd(['rm'] + remote_paths)
|
clean_cmd = pmb.helpers.run_core.flat_cmd([['rm'] + remote_paths])
|
||||||
add_cmd_complete = shlex.quote(f"{add_cmd}; rc=$?; {clean_cmd}; exit $rc")
|
add_cmd_complete = shlex.quote(f"{add_cmd}; rc=$?; {clean_cmd}; exit $rc")
|
||||||
# Run apk command in a subshell in case the foreign device has a non-POSIX shell.
|
# Run apk command in a subshell in case the foreign device has a non-POSIX shell.
|
||||||
command = ['ssh', '-t', '-p', port, f'{user}@{host}',
|
command = ['ssh', '-t', '-p', port, f'{user}@{host}',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue