diff --git a/airootfs/etc/systemd/scripts/sysrescue-initialize.py b/airootfs/etc/systemd/scripts/sysrescue-initialize.py index 47123ba..3148b83 100755 --- a/airootfs/etc/systemd/scripts/sysrescue-initialize.py +++ b/airootfs/etc/systemd/scripts/sysrescue-initialize.py @@ -8,6 +8,29 @@ import glob import os import sys import re +import tempfile + +# pythons os.symlink bails when a file already exists, this function also handles overwrites +def symlink_overwrite(target, link_file): + link_dir = os.path.dirname(link_file) + + while True: + # get a tmp filename in the same dir as link_file + tmp = tempfile.NamedTemporaryFile(delete=True, dir=link_dir) + tmp.close() + # tmp is now deleted + + # os.symlink aborts when a file with the same name already exists + # someone could have created a new file with the tmp name right in this moment + # so we need to loop and try again in this case + try: + os.symlink(target,tmp.name) + break + except FileExistsError: + pass + + os.replace(tmp.name, link_file) + # ============================================================================== # Initialization @@ -179,6 +202,64 @@ if (late_load_srm != None) and (late_load_srm != ""): # so we have to do this manually. Note: only affects multi-user.target, nothing else subprocess.run(["/usr/bin/systemctl", "--no-block", "start", "multi-user.target"]) +# ============================================================================== +# autoterminal: programs that take over a virtual terminal for user interaction +# ============================================================================== + +# expect a dict with terminal-name: command, like config['autoterminal']['tty2'] = "/usr/bin/setkmap" +if ('autoterminal' in config) and (config['autoterminal'] is not None) and \ + (config['autoterminal'] is not False) and isinstance(config['autoterminal'], dict): + print("====> Configuring autoterminal ...") + with open('/usr/share/sysrescue/template/autoterminal.service', 'r') as template_file: + conf_template = template_file.read() + with open('/usr/share/sysrescue/template/serial-autoterminal.service', 'r') as template_file: + serial_conf_template = template_file.read() + start_services = [] + for terminal, command in sorted(config['autoterminal'].items()): + if m := re.match(r"^serial:([a-zA-Z0-9_-]+)$", terminal): + serial=True + terminal = m.group(1) + else: + serial=False + + if not re.match(r"^[a-zA-Z0-9_-]+$", terminal): + print (f"Ignoring invalid terminal name '{terminal}'") + errcnt+=1 + continue + # do not check if terminal or command exists: an autorun could create them later on + if serial: + print (f"setting serial terminal '{terminal}' to '{command}'") + else: + print (f"setting terminal '{terminal}' to '{command}'") + with open(f"/etc/systemd/system/autoterminal-{terminal}.service", "w") as terminal_conf: + # write service config, based on the template config we loaded above + # don't use getty@{terminal}.service name to not use autovt@{terminal}.service on-demand logic + if serial: + conf_data=serial_conf_template.replace("%TTY%",terminal) + else: + conf_data=conf_template.replace("%TTY%",terminal) + + conf_data=conf_data.replace("%EXEC%",command) + terminal_conf.write(conf_data) + # enable service: always start it, do not wait for the user to switch to the terminal + # means other programs (like X.org) can't allocate it away, also allows for longer running init sequences + symlink_overwrite(f"/etc/systemd/system/autoterminal-{terminal}.service", + f"/etc/systemd/system/getty.target.wants/autoterminal-{terminal}.service") + + # mask the regular getty for this terminal + if serial: + symlink_overwrite("/dev/null",f"/etc/systemd/system/serial-getty@{terminal}.service") + else: + symlink_overwrite("/dev/null",f"/etc/systemd/system/getty@{terminal}.service") + symlink_overwrite("/dev/null",f"/etc/systemd/system/autovt@{terminal}.service") + + start_services.append(f"autoterminal-{terminal}.service") + # reload systemd to allow the new config to take effect + subprocess.run(["/usr/bin/systemctl", "daemon-reload"]) + # explicitly start new services (after daemon-reload): systemd can't update dependencies while starting + for s in start_services: + subprocess.run(["/usr/bin/systemctl", "--no-block", "start", s]) + # ============================================================================== # End of the script # ============================================================================== diff --git a/airootfs/etc/systemd/system/getty@.service.d/autologin.conf b/airootfs/etc/systemd/system/getty@.service.d/10-autologin.conf similarity index 100% rename from airootfs/etc/systemd/system/getty@.service.d/autologin.conf rename to airootfs/etc/systemd/system/getty@.service.d/10-autologin.conf diff --git a/airootfs/etc/systemd/system/serial-getty@.service.d/autologin.conf b/airootfs/etc/systemd/system/serial-getty@.service.d/10-autologin.conf similarity index 100% rename from airootfs/etc/systemd/system/serial-getty@.service.d/autologin.conf rename to airootfs/etc/systemd/system/serial-getty@.service.d/10-autologin.conf diff --git a/airootfs/usr/share/sysrescue/template/autoterminal.service b/airootfs/usr/share/sysrescue/template/autoterminal.service new file mode 100644 index 0000000..769b51b --- /dev/null +++ b/airootfs/usr/share/sysrescue/template/autoterminal.service @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of SystemRescue, based on getty@.service from systemd +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=SystemRescue autoterminal %TTY% +Documentation=https://www.system-rescue.org/manual/autoterminal/ +After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target \ + getty@%TTY%.service autovt@%TTY%.service + +# If additional gettys are spawned during boot then we should make +# sure that this is synchronized before getty.target, even though +# getty.target didn't actually pull it in. +Before=getty.target +IgnoreOnIsolate=yes + +# IgnoreOnIsolate causes issues with sulogin, if someone isolates +# rescue.target or starts rescue.service from multi-user.target or +# graphical.target. +Conflicts=rescue.service getty@%TTY%.service autovt@%TTY%.service +Before=rescue.service + +[Service] +# the VT is cleared by TTYVTDisallocate +ExecStart=-%EXEC% + +# do not wait 5 seconds as for Type=idle before starting the service +Type=simple + +Restart=always +RestartSec=1 +UtmpIdentifier=%TTY% +StandardInput=tty +StandardOutput=tty +TTYPath=/dev/%TTY% +TTYReset=yes +TTYVHangup=yes +TTYVTDisallocate=yes +IgnoreSIGPIPE=no +SendSIGHUP=yes + +# make this a systemd-logind session without needing a getty +User=root +PAMName=login + +# generate all utmp/wtmp entries and don't expect the program to do it +UtmpMode=user + +# Unset locale for the console getty since the console has problems +# displaying some internationalized messages. +UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY \ + LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION + +[Install] +WantedBy=getty.target diff --git a/airootfs/usr/share/sysrescue/template/serial-autoterminal.service b/airootfs/usr/share/sysrescue/template/serial-autoterminal.service new file mode 100644 index 0000000..c5a7bcb --- /dev/null +++ b/airootfs/usr/share/sysrescue/template/serial-autoterminal.service @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of SystemRescue, based on serial-getty@.service from systemd +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=SystemRescue serial autoterminal %TTY% +Documentation=https://www.system-rescue.org/manual/autoterminal/ +BindsTo=dev-%TTY%.device +After=dev-%TTY%.device systemd-user-sessions.service plymouth-quit-wait.service \ + getty-pre.target serial-getty@%TTY%.service + +# If additional gettys are spawned during boot then we should make +# sure that this is synchronized before getty.target, even though +# getty.target didn't actually pull it in. +Before=getty.target +IgnoreOnIsolate=yes + +# IgnoreOnIsolate causes issues with sulogin, if someone isolates +# rescue.target or starts rescue.service from multi-user.target or +# graphical.target. +Conflicts=rescue.service serial-getty@%TTY%.service +Before=rescue.service + +[Service] +ExecStart=-%EXEC% + +# do not wait 5 seconds as for Type=idle before starting the service +Type=simple + +Restart=always +RestartSec=1 +UtmpIdentifier=%TTY% +StandardInput=tty +StandardOutput=tty +TTYPath=/dev/%TTY% +TTYReset=yes +TTYVHangup=yes +IgnoreSIGPIPE=no +SendSIGHUP=yes + +# make this a systemd-logind session without needing a getty +User=root +PAMName=login + +# generate all utmp/wtmp entries and don't expect the program to do it +UtmpMode=user + +[Install] +WantedBy=getty.target