Pass more status codes and arguments

This commit is contained in:
Jip-Hop 2024-02-08 01:15:38 +01:00
parent d94a2aac7d
commit 00e98ac07d
2 changed files with 143 additions and 90 deletions

View File

@ -61,7 +61,7 @@ exit
From the TrueNAS host, open a shell as the rootless user inside the jail. From the TrueNAS host, open a shell as the rootless user inside the jail.
```bash ```bash
machinectl shell --uid 1000 rootless jlmkr shell --uid 1000 rootless
``` ```
Run rootless podman as user 1000. Run rootless podman as user 1000.

231
jlmkr.py
View File

@ -223,22 +223,20 @@ def exec_jail(jail_name, cmd, args):
""" """
Execute a command in the jail with given name. Execute a command in the jail with given name.
""" """
sys.exit( return subprocess.run(
subprocess.run( [
[ "systemd-run",
"systemd-run", "--machine",
"--machine", jail_name,
jail_name, "--quiet",
"--quiet", "--pipe",
"--pipe", "--wait",
"--wait", "--collect",
"--collect", "--service-type=exec",
"--service-type=exec", cmd,
cmd, ]
] + args
+ args ).returncode
).returncode
)
def status_jail(jail_name): def status_jail(jail_name):
@ -246,28 +244,32 @@ def status_jail(jail_name):
Show the status of the systemd service wrapping the jail with given name. Show the status of the systemd service wrapping the jail with given name.
""" """
# Alternatively `machinectl status jail_name` could be used # Alternatively `machinectl status jail_name` could be used
subprocess.run(["systemctl", "status", f"{SYMLINK_NAME}-{jail_name}"]) return subprocess.run(
["systemctl", "status", f"{SYMLINK_NAME}-{jail_name}"]
).returncode
def log_jail(jail_name): def log_jail(jail_name):
""" """
Show the log file of the jail with given name. Show the log file of the jail with given name.
""" """
subprocess.run(["journalctl", "-u", f"{SYMLINK_NAME}-{jail_name}"]) return subprocess.run(
["journalctl", "-u", f"{SYMLINK_NAME}-{jail_name}"]
).returncode
def shell_jail(jail_name): def shell_jail(args):
""" """
Open a shell in the jail with given name. Open a shell in the jail with given name.
""" """
subprocess.run(["machinectl", "shell", jail_name]) return subprocess.run(["machinectl", "shell"] + args).returncode
def stop_jail(jail_name): def stop_jail(jail_name):
""" """
Stop jail with given name. Stop jail with given name.
""" """
subprocess.run(["machinectl", "poweroff", jail_name]) return subprocess.run(["machinectl", "poweroff", jail_name]).returncode
def parse_config(jail_config_path): def parse_config(jail_config_path):
@ -293,7 +295,8 @@ def start_jail(jail_name, check_startup_enabled=False):
) )
if not check_startup_enabled and jail_is_running(jail_name): if not check_startup_enabled and jail_is_running(jail_name):
fail(skip_start_message) eprint(skip_start_message)
return 1
jail_path = get_jail_path(jail_name) jail_path = get_jail_path(jail_name)
jail_config_path = get_jail_config_path(jail_name) jail_config_path = get_jail_config_path(jail_name)
@ -301,7 +304,8 @@ def start_jail(jail_name, check_startup_enabled=False):
config = parse_config(jail_config_path) config = parse_config(jail_config_path)
if not config: if not config:
fail("Aborting...") eprint("Aborting...")
return 1
# Only start if the startup setting is enabled in the config # Only start if the startup setting is enabled in the config
if check_startup_enabled: if check_startup_enabled:
@ -310,10 +314,10 @@ def start_jail(jail_name, check_startup_enabled=False):
if jail_is_running(jail_name): if jail_is_running(jail_name):
# ...but we can skip if it's already running # ...but we can skip if it's already running
eprint(skip_start_message) eprint(skip_start_message)
return return 0
else: else:
# Skip starting this jail since the startup config setting isnot enabled # Skip starting this jail since the startup config setting is not enabled
return return 0
systemd_run_additional_args = [ systemd_run_additional_args = [
f"--unit={SYMLINK_NAME}-{jail_name}", f"--unit={SYMLINK_NAME}-{jail_name}",
@ -434,8 +438,9 @@ def start_jail(jail_name, check_startup_enabled=False):
) )
) )
if subprocess.run(cmd).returncode != 0: returncode = subprocess.run(cmd).returncode
fail( if returncode != 0:
eprint(
dedent( dedent(
f""" f"""
Failed to start jail {jail_name}... Failed to start jail {jail_name}...
@ -445,6 +450,8 @@ def start_jail(jail_name, check_startup_enabled=False):
) )
) )
return returncode
def cleanup(jail_path): def cleanup(jail_path):
""" """
@ -505,7 +512,8 @@ def run_lxc_download_script(
lxc_download_script, lxc_download_script,
) )
if not validate_sha256(lxc_download_script, DOWNLOAD_SCRIPT_DIGEST): if not validate_sha256(lxc_download_script, DOWNLOAD_SCRIPT_DIGEST):
fail("Abort! Downloaded script has unexpected contents.") eprint("Abort! Downloaded script has unexpected contents.")
return 1
stat_chmod(lxc_download_script, 0o700) stat_chmod(lxc_download_script, 0o700)
@ -541,7 +549,10 @@ def run_lxc_download_script(
p1.wait() p1.wait()
if check_exit_code and p1.returncode != 0: if check_exit_code and p1.returncode != 0:
fail("Aborting...") eprint("Aborting...")
return p1.returncode
return 0
def stat_chmod(file_path, mode): def stat_chmod(file_path, mode):
@ -625,7 +636,7 @@ def create_jail(jail_name, distro="debian", release="bookworm"):
print(DISCLAIMER) print(DISCLAIMER)
if os.path.basename(os.getcwd()) != "jailmaker": if os.path.basename(os.getcwd()) != "jailmaker":
fail( eprint(
dedent( dedent(
f""" f"""
{COMMAND_NAME} needs to create files. {COMMAND_NAME} needs to create files.
@ -634,6 +645,7 @@ def create_jail(jail_name, distro="debian", release="bookworm"):
Please create a dedicated directory called 'jailmaker', store {SCRIPT_NAME} there and try again.""" Please create a dedicated directory called 'jailmaker', store {SCRIPT_NAME} there and try again."""
) )
) )
return 1
if not PurePath(get_mount_point(os.getcwd())).is_relative_to("/mnt"): if not PurePath(get_mount_point(os.getcwd())).is_relative_to("/mnt"):
print( print(
@ -649,7 +661,8 @@ def create_jail(jail_name, distro="debian", release="bookworm"):
) )
) )
if not agree("Do you wish to ignore this warning and continue?", "n"): if not agree("Do you wish to ignore this warning and continue?", "n"):
fail("Aborting...") eprint("Aborting...")
return 0
# Create the dir where to store the jails # Create the dir where to store the jails
os.makedirs(JAILS_DIR_PATH, exist_ok=True) os.makedirs(JAILS_DIR_PATH, exist_ok=True)
@ -671,7 +684,9 @@ def create_jail(jail_name, distro="debian", release="bookworm"):
input("Press Enter to continue...") input("Press Enter to continue...")
print() print()
run_lxc_download_script() returncode = run_lxc_download_script()
if returncode != 0:
return returncode
print( print(
dedent( dedent(
@ -834,7 +849,11 @@ def create_jail(jail_name, distro="debian", release="bookworm"):
# but we don't need it so we will remove it later # but we don't need it so we will remove it later
open(jail_config_path, "a").close() open(jail_config_path, "a").close()
run_lxc_download_script(jail_name, jail_path, jail_rootfs_path, distro, release) returncode = run_lxc_download_script(
jail_name, jail_path, jail_rootfs_path, distro, release
)
if returncode != 0:
return returncode
# Assuming the name of your jail is "myjail" # Assuming the name of your jail is "myjail"
# and "machinectl shell myjail" doesn't work # and "machinectl shell myjail" doesn't work
@ -887,7 +906,7 @@ def create_jail(jail_name, distro="debian", release="bookworm"):
) )
if agree("Abort creating jail?", "y"): if agree("Abort creating jail?", "y"):
exit(1) return 1
with contextlib.suppress(FileNotFoundError): with contextlib.suppress(FileNotFoundError):
# Remove config which systemd handles for us # Remove config which systemd handles for us
@ -1015,7 +1034,7 @@ def create_jail(jail_name, distro="debian", release="bookworm"):
print() print()
if agree(f"Do you want to start jail {jail_name} right now?", "y"): if agree(f"Do you want to start jail {jail_name} right now?", "y"):
start_jail(jail_name) return start_jail(jail_name)
def jail_is_running(jail_name): def jail_is_running(jail_name):
@ -1033,18 +1052,34 @@ def edit_jail(jail_name):
""" """
Edit jail with given name. Edit jail with given name.
""" """
if check_jail_name_valid(jail_name):
if check_jail_name_available(jail_name, False): if not check_jail_name_valid(jail_name):
eprint(f"A jail with name {jail_name} does not exist.") return 1
else:
jail_config_path = get_jail_config_path(jail_name) if check_jail_name_available(jail_name, False):
if not shutil.which(TEXT_EDITOR): eprint(f"A jail with name {jail_name} does not exist.")
eprint(f"Unable to edit config file: {jail_config_path}.") return 1
eprint(f"The {TEXT_EDITOR} text editor is not available.")
else: jail_config_path = get_jail_config_path(jail_name)
subprocess.run([TEXT_EDITOR, get_jail_config_path(jail_name)]) if not shutil.which(TEXT_EDITOR):
if jail_is_running(jail_name): eprint(
print("\nRestart the jail for edits to apply (if you made any).") f"Unable to edit config file: {jail_config_path}.",
f"\nThe {TEXT_EDITOR} text editor is not available",
)
return 1
returncode = subprocess.run(
[TEXT_EDITOR, get_jail_config_path(jail_name)]
).returncode
if returncode != 0:
eprint("An error occurred while editing the jail config.")
return returncode
if jail_is_running(jail_name):
print("\nRestart the jail for edits to apply (if you made any).")
return 0
def remove_jail(jail_name): def remove_jail(jail_name):
@ -1052,28 +1087,31 @@ def remove_jail(jail_name):
Remove jail with given name. Remove jail with given name.
""" """
if check_jail_name_valid(jail_name): if not check_jail_name_valid(jail_name):
if check_jail_name_available(jail_name, False): return 1
eprint(f"A jail with name {jail_name} does not exist.")
else:
check = (
input(f'\nCAUTION: Type "{jail_name}" to confirm jail deletion!\n\n')
or ""
)
if check == jail_name:
jail_path = get_jail_path(jail_name)
if jail_is_running(jail_name):
print(f"\nWait for {jail_name} to stop...", end="")
stop_jail(jail_name)
# Need to sleep since deleting immediately after stop causes problems...
while jail_is_running(jail_name):
time.sleep(1)
print(".", end="", flush=True)
print(f"\nCleaning up: {jail_path}") if check_jail_name_available(jail_name, False):
shutil.rmtree(jail_path) eprint(f"A jail with name {jail_name} does not exist.")
else: return 1
eprint("Wrong name, nothing happened.")
check = input(f'\nCAUTION: Type "{jail_name}" to confirm jail deletion!\n\n')
if check == jail_name:
jail_path = get_jail_path(jail_name)
if jail_is_running(jail_name):
print(f"\nWait for {jail_name} to stop...", end="")
stop_jail(jail_name)
# Need to sleep since deleting immediately after stop causes problems...
while jail_is_running(jail_name):
time.sleep(1)
print(".", end="", flush=True)
print(f"\nCleaning up: {jail_path}")
shutil.rmtree(jail_path)
return 0
else:
eprint("Wrong name, nothing happened.")
return 1
def print_table(header, list_of_objects, empty_value_indicator): def print_table(header, list_of_objects, empty_value_indicator):
@ -1106,7 +1144,7 @@ def run_command_and_parse_json(command):
parsed_output = json.loads(output) parsed_output = json.loads(output)
return parsed_output return parsed_output
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
print(f"Error parsing JSON: {e}") eprint(f"Error parsing JSON: {e}")
return None return None
@ -1131,7 +1169,7 @@ def list_jails():
if not jail_names: if not jail_names:
print("No jails.") print("No jails.")
return return 0
for jail in jail_names: for jail in jail_names:
jails[jail] = {"name": jail, "running": False} jails[jail] = {"name": jail, "running": False}
@ -1177,6 +1215,8 @@ def list_jails():
empty_value_indicator, empty_value_indicator,
) )
return 0
def install_jailmaker(): def install_jailmaker():
# Check if command exists in path # Check if command exists in path
@ -1221,11 +1261,22 @@ def install_jailmaker():
print("Done installing jailmaker.") print("Done installing jailmaker.")
return 0
def startup_jails(): def startup_jails():
install_jailmaker() returncode = install_jailmaker()
if returncode != 0:
eprint("Failed to install jailmaker. Abort startup.")
return returncode
for jail_name in get_all_jail_names(): for jail_name in get_all_jail_names():
start_jail(jail_name, True) returncode = start_jail(jail_name, True)
eprint(f"Failed to start jail {jail_name}. Abort startup.")
return returncode
return 0
def main(): def main():
@ -1255,8 +1306,10 @@ def main():
).add_argument("name", help="name of the jail") ).add_argument("name", help="name of the jail")
subparsers.add_parser( subparsers.add_parser(
name="shell", epilog=DISCLAIMER, help="open shell in running jail" name="shell",
).add_argument("name", help="name of the jail") epilog=DISCLAIMER,
help="open shell in running jail (alias for machinectl shell)",
)
exec_parser = subparsers.add_parser( exec_parser = subparsers.add_parser(
name="exec", epilog=DISCLAIMER, help="execute a command in the jail" name="exec", epilog=DISCLAIMER, help="execute a command in the jail"
@ -1313,48 +1366,48 @@ def main():
args, additional_args = parser.parse_known_args() args, additional_args = parser.parse_known_args()
if args.subcommand == "install": if args.subcommand == "install":
install_jailmaker() sys.exit(install_jailmaker())
elif args.subcommand == "create": elif args.subcommand == "create":
create_jail(args.name) sys.exit(create_jail(args.name))
elif args.subcommand == "start": elif args.subcommand == "start":
start_jail(args.name) sys.exit(start_jail(args.name))
elif args.subcommand == "shell": elif args.subcommand == "shell":
shell_jail(args.name) sys.exit(shell_jail(additional_args))
elif args.subcommand == "exec": elif args.subcommand == "exec":
exec_jail(args.name, args.cmd, additional_args) sys.exit(exec_jail(args.name, args.cmd, additional_args))
elif args.subcommand == "status": elif args.subcommand == "status":
status_jail(args.name) sys.exit(status_jail(args.name))
elif args.subcommand == "log": elif args.subcommand == "log":
log_jail(args.name) sys.exit(log_jail(args.name))
elif args.subcommand == "stop": elif args.subcommand == "stop":
stop_jail(args.name) sys.exit(stop_jail(args.name))
elif args.subcommand == "edit": elif args.subcommand == "edit":
edit_jail(args.name) sys.exit(edit_jail(args.name))
elif args.subcommand == "remove": elif args.subcommand == "remove":
remove_jail(args.name) sys.exit(remove_jail(args.name))
elif args.subcommand == "list": elif args.subcommand == "list":
list_jails() sys.exit(list_jails())
elif args.subcommand == "images": elif args.subcommand == "images":
run_lxc_download_script() sys.exit(run_lxc_download_script())
elif args.subcommand == "startup": elif args.subcommand == "startup":
startup_jails() sys.exit(startup_jails())
else: else:
if agree("Create a new jail?", "y"): if agree("Create a new jail?", "y"):
print() print()
create_jail("") sys.exit(create_jail(""))
else: else:
parser.print_usage() parser.print_usage()