Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package virtme for openSUSE:Leap:16.0 checked in at 2025-08-11 16:12:43 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Leap:16.0/virtme (Old) and /work/SRC/openSUSE:Leap:16.0/.virtme.new.1085 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "virtme" Mon Aug 11 16:12:43 2025 rev:4 rq:1298845 version:1.37 Changes: -------- --- /work/SRC/openSUSE:Leap:16.0/virtme/virtme.changes 2025-06-04 11:24:14.562329251 +0200 +++ /work/SRC/openSUSE:Leap:16.0/.virtme.new.1085/virtme.changes 2025-08-11 16:13:25.897934340 +0200 @@ -1,0 +2,12 @@ +Mon Aug 11 06:27:20 UTC 2025 - Michael Vetter <mvet...@suse.com> + +- Update to 1.37: + The most interesting feature in this new version is the initial + support for systemd. + Until now, virtme-ng didn’t support systemd because it relied on a custom + init system (virtme-ng-init) to speed up boot time. As a result, tests + requiring systemd couldn't run inside the virtme-ng session. With the new + --systemd option, virtme-ng can now (optionally) boot with systemd in the + virtualized environment, enabling full systemd interaction during testing. + +------------------------------------------------------------------- Old: ---- virtme-ng-1.36.tar.xz New: ---- virtme-ng-1.37.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ virtme.spec ++++++ --- /var/tmp/diff_new_pack.n7wwzr/_old 2025-08-11 16:13:26.213947983 +0200 +++ /var/tmp/diff_new_pack.n7wwzr/_new 2025-08-11 16:13:26.213947983 +0200 @@ -1,7 +1,7 @@ # # spec file for package virtme # -# Copyright (c) 2025 SUSE LLC +# Copyright (c) 2025 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -22,7 +22,7 @@ %global pythons python311 %endif Name: virtme -Version: 1.36 +Version: 1.37 Release: 0 Summary: Tools for virtualize the running distro or a rootfs License: GPL-2.0-only @@ -81,5 +81,4 @@ %{python_sitelib}/%{name}_ng %{python_sitelib}/%{name}_ng-%{version}-py*.egg-info %{_datadir}/bash-completion -%config(noreplace) %{_sysconfdir}/%{name}-ng.conf ++++++ _service ++++++ --- /var/tmp/diff_new_pack.n7wwzr/_old 2025-08-11 16:13:26.257949882 +0200 +++ /var/tmp/diff_new_pack.n7wwzr/_new 2025-08-11 16:13:26.261950055 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/arighi/virtme-ng.git</param> <param name="scm">git</param> <param name="submodules">enable</param> - <param name="revision">v1.36</param> + <param name="revision">v1.37</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="versionrewrite-replacement">\1</param> ++++++ virtme-ng-1.36.tar.xz -> virtme-ng-1.37.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.36/.pre-commit-config.yaml new/virtme-ng-1.37/.pre-commit-config.yaml --- old/virtme-ng-1.36/.pre-commit-config.yaml 2025-05-11 16:37:00.000000000 +0200 +++ new/virtme-ng-1.37/.pre-commit-config.yaml 2025-08-08 09:40:53.000000000 +0200 @@ -11,7 +11,7 @@ - id: check-executables-have-shebangs - id: check-shebang-scripts-are-executable - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.8 + rev: v0.12.7 hooks: - id: ruff args: [--fix] @@ -21,6 +21,6 @@ hooks: - id: shellcheck - repo: https://github.com/scop/pre-commit-shfmt - rev: v3.11.0-1 + rev: v3.12.0-2 hooks: - id: shfmt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.36/README.md new/virtme-ng-1.37/README.md --- old/virtme-ng-1.36/README.md 2025-05-11 16:37:00.000000000 +0200 +++ new/virtme-ng-1.37/README.md 2025-08-08 09:40:53.000000000 +0200 @@ -103,6 +103,25 @@ $ ./vng --help ``` +Configuration +============= + +* You may customize the default configuration by providing one of the + following, by order of preference: `$HOME/.config/virtme-ng/virtme-ng.conf`, + `$HOME/.virtme-ng.conf` or `/etc/virtme-ng.conf`. As a fallback for any + missing values, the default ones will be used. + +* The format of the file is JSON. Default values: +``` +{ + "default_opts": {}, + "systemd": { + "masks": ["getty@"] + } +} +``` + + Requirements ============ @@ -294,6 +313,36 @@ Linux version 6.7.0-060700rc5-generic (kernel@kathleen) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.2.0-7ubuntu1) 13.2.0, GNU ld (GNU Binutils for Ubuntu) 2.41) #202312102332 SMP PREEMPT_DYNAMIC Sun Dec 10 23:41:31 UTC 2023 ``` + - Run with systemd as init: +``` + $ sudo vng -r --systemd --exec "systemctl status | head" + ● virtme-ng + State: starting + Units: 392 loaded (incl. loaded aliases) + Jobs: 4 queued + Failed: 3 units + Since: Mon 2025-05-26 11:00:47 -03; 4s ago + systemd: 257.5+suse.8.gc10a66fb4d + Tainted: unmerged-bin + CGroup: / + ├─init.scope +``` + + - Run with systemd as init in an external rootfs: +``` + $ vng -r --systemd --user root --root ./rootfs/sid --exec "systemctl status | head" + ● virtme-ng + State: degraded + Units: 273 loaded (incl. loaded aliases) + Jobs: 0 queued + Failed: 4 units + Since: Mon 2025-05-26 14:01:06 UTC; 2s ago + systemd: 257.5-2 + Tainted: unmerged-bin + CGroup: / + ├─init.scope +``` + - Run the current kernel creating a 1GB NUMA node with CPUs 0,1,3 assigned and a 3GB NUMA node with CPUs 2,4,5,6,7 assigned: ``` @@ -520,12 +569,12 @@ Typically, if you always use virtme-ng with an external build server (e.g., `vng --build --build-host REMOTE_SERVER --build-host-exec-prefix CMD`) you don't always want to specify these options, so instead, you can simply define -them in `~/.config/virtme-ng/virtme-ng.conf` under `default_opts` and then -simply run `vng --build`. +them in your configuration file (refer to the [Configuration](#configuration) +section) under `default_opts` and then simply run `vng --build`. Example (always use an external build server called 'kathleen' and run make inside a build chroot called `chroot:lunar-amd64`). To do so, add the -`default_opts` section in `~/.config/virtme-ng/virtme-ng.conf` as following: +`default_opts` section in your configuration file as following: ``` { "default_opts": { @@ -584,9 +633,17 @@ ``` - Snap support is still experimental and something may not work as expected - (keep in mind that virtme-ng will try to run snapd in a bare minimum system - environment without systemd), if some snaps are not running try to disable - apparmor, adding `--append="apparmor=0"` to the virtme-ng command line. + (keep in mind that, by default, virtme-ng will try to run snapd in a bare + minimum system environment without systemd), if some snaps are not running + try to disable apparmor, adding `--append="apparmor=0"` to the virtme-ng + command line. + + - Systemd support (`--systemd`) is still experimental. If something does not + work for you, try masking the unit that is freezing, e.g. `--append + "systemd.mask=$PROBLEMATIC_UNIT"` (refer to the + [Configuration](#configuration) section for a more permanent setup). Be + aware that you might also need `--user root`, or if you're using your own + `/` as ROOTFS, you may need to run vng itself as root. - Running virtme-ng instances inside docker: in case of failures/issues, especially with stdin/stdout/stderr redirections, make sure that you have diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.36/cfg/virtme-ng.conf new/virtme-ng-1.37/cfg/virtme-ng.conf --- old/virtme-ng-1.36/cfg/virtme-ng.conf 2025-05-11 16:37:00.000000000 +0200 +++ new/virtme-ng-1.37/cfg/virtme-ng.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,4 +0,0 @@ -{ - "default_opts" : { - } -} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.36/setup.py new/virtme-ng-1.37/setup.py --- old/virtme-ng-1.36/setup.py 2025-05-11 16:37:00.000000000 +0200 +++ new/virtme-ng-1.37/setup.py 2025-08-08 09:40:53.000000000 +0200 @@ -124,7 +124,6 @@ packages.append("virtme.guest.bin") data_files = [ - ("/etc", ["cfg/virtme-ng.conf"]), ("/usr/share/bash-completion/completions", ["virtme-ng-prompt", "vng-prompt"]), ] if build_manpages: @@ -146,7 +145,7 @@ author_email="ari...@nvidia.com", description="Build and run a kernel inside a virtualized snapshot of your live system", url="https://github.com/arighi/virtme-ng", - license="GPLv2", + license="GPL-2.0-only", long_description=open( os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf-8" ).read(), @@ -178,7 +177,6 @@ "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: System Administrators", - "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "Operating System :: POSIX :: Linux", ], zip_safe=False, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.36/virtme/commands/run.py new/virtme-ng-1.37/virtme/commands/run.py --- old/virtme-ng-1.36/virtme/commands/run.py 2025-05-11 16:37:00.000000000 +0200 +++ new/virtme-ng-1.37/virtme/commands/run.py 2025-08-08 09:40:53.000000000 +0200 @@ -25,11 +25,14 @@ from typing import Any, Dict, List, NoReturn, Optional, Tuple from virtme_ng.utils import ( + CACHE_DIR, DEFAULT_VIRTME_SSH_HOSTNAME_CID_SEPARATOR, + SERIAL_GETTY_FILE, SSH_CONF_FILE, SSH_DIR, VIRTME_SSH_DESTINATION_NAME, VIRTME_SSH_HOSTNAME_CID_SEPARATORS, + get_conf, ) from .. import architectures, mkinitramfs, modfinder, qemu_helpers, resources, virtmods @@ -98,6 +101,11 @@ "--root", action="store", default="/", help="Local path to use as guest root" ) g.add_argument( + "--systemd", + action="store_true", + help="Execute systemd as init (EXPERIMENTAL)", + ) + g.add_argument( "--rw", action="store_true", help="Give the guest read-write access to its root filesystem", @@ -929,8 +937,10 @@ try: # Run the 'file' command on the binary and check for the string # "statically linked" - result = subprocess.check_output(["file", binary_path], universal_newlines=True) - return "statically linked" in result + result = subprocess.check_output( + ["file", "-L", binary_path], universal_newlines=True + ) + return "statically linked" in result or "static-pie linked" in result except subprocess.CalledProcessError: return False @@ -1161,12 +1171,17 @@ if kernel.version: print(f"kernel version = {kernel.version}") vmlinux = "" - if os.path.exists("vmlinux"): + if args.kdir is not None and os.path.exists(f"{args.kdir}/vmlinux"): + vmlinux = f"{args.kdir}/vmlinux" + elif os.path.exists("vmlinux"): vmlinux = "vmlinux" elif os.path.exists(f"/usr/lib/debug/boot/vmlinux-{kernel.version}"): vmlinux = f"/usr/lib/debug/boot/vmlinux-{kernel.version}" command = ["gdb", "-q", "-ex", "target remote localhost:1234", vmlinux] - os.execvp("gdb", command) + if args.dry_run: + print(shlex.join(command)) + else: + os.execvp("gdb", command) sys.exit(0) qemuargs: List[str] = [qemu.qemubin] @@ -1189,9 +1204,10 @@ try: with open("/proc/sys/fs/nr_open", encoding="utf-8") as file: nr_open = file.readline().strip() - kernelargs.append(f"nr_open={nr_open}") - except FileNotFoundError: + except (FileNotFoundError, PermissionError): pass + else: + kernelargs.append(f"nr_open={nr_open}") # Parse NUMA settings. if args.numa: @@ -1296,28 +1312,59 @@ else: virtme_init_cmd = "virtme-init" + if args.systemd: + # disable systemd-fstab-generator so boot does not freeze while waiting for disks + kernelargs.append("fstab=no") + # disable systemd-cryptsetup-generator so it doesn't wait for encrypted disks + kernelargs.append("luks=no") + # disable auditd so there are no errors if the user lacks `--rw` + kernelargs.append("audit=off") + kernelargs.extend( + [f"console={console}" for console in arch.serial_console_args() or []], + ) + kernelargs.extend( + [f"systemd.mask={unit}" for unit in get_conf("systemd.masks") or []] + ) + if args.root == "/": - initcmds = [f"init={guest_tools_path}/{virtme_init_cmd}"] + if args.systemd: + initcmds = [ + "init=/bin/sh", + "--", + "-c", + f"SYSTEMD_UNIT_PATH={CACHE_DIR}: exec /sbin/init;", + ] + else: + initcmds = [f"init={guest_tools_path}/{virtme_init_cmd}"] else: virtfs_config = VirtFSConfig( path=guest_tools_path, mount_tag="virtme.guesttools", ) export_virtfs(qemu, arch, qemuargs, virtfs_config) - initcmds = [ - "init=/bin/sh", - "--", - "-c", - ";".join( + initsh = [ + "mount -t tmpfs run /run", + "mkdir -p /run/virtme/guesttools", + "/bin/mount -n -t 9p -o ro,version=9p2000.L,trans=virtio,access=any " + + "virtme.guesttools /run/virtme/guesttools", + ] + if args.systemd: + virtfs_config = VirtFSConfig( + path=str(CACHE_DIR), + mount_tag="virtme.cache", + ) + export_virtfs(qemu, arch, qemuargs, virtfs_config) + initsh.extend( [ - "mount -t tmpfs run /run", - "mkdir -p /run/virtme/guesttools", + "mkdir -p /run/virtme/cache", "/bin/mount -n -t 9p -o ro,version=9p2000.L,trans=virtio,access=any " - + "virtme.guesttools /run/virtme/guesttools", - f"exec /run/virtme/guesttools/{virtme_init_cmd}", + + "virtme.cache /run/virtme/cache", + "SYSTEMD_UNIT_PATH=/run/virtme/cache: exec /sbin/init", ] - ), - ] + ) + else: + initsh.append(f"exec /run/virtme/guesttools/{virtme_init_cmd}") + initcmds = ["init=/bin/sh", "--", "-c", "; ".join(initsh)] # Arrange for modules to end up in the right place if kernel.moddir is not None: @@ -1400,12 +1447,15 @@ if args.graphics is None and not args.script_sh and not args.script_exec: qemuargs.extend(["-echr", "1"]) - # Redirect kernel errors to stderr, creating a separate console. - # - # If we don't have access to stderr via procfs (for example when - # running inside a container), print a warning and implicitly - # suppress the kernel errors redirection. - if can_access_file("/proc/self/fd/2"): + if args.systemd: + # Do nothing if `--systemd` is used, since it relies on the serial console + pass + elif can_access_file("/proc/self/fd/2"): + # Redirect kernel errors to stderr, creating a separate console. + # + # If we don't have access to stderr via procfs (for example when + # running inside a container), print a warning and implicitly + # suppress the kernel errors redirection. qemuargs.extend(["-chardev", "file,path=/proc/self/fd/2,id=dmesg"]) qemuargs.extend(["-device", arch.virtio_dev_type("serial")]) qemuargs.extend(["-device", "virtconsole,chardev=dmesg"]) @@ -1841,6 +1891,28 @@ # Load a normal kernel qemuargs.extend(["-kernel", kernel.kimg]) if kernelargs: + if args.systemd: + init_environment_vars = [] + for arg in kernelargs: + match = re.match(r"(virtme_.*)=(.*)", arg) + if not match: + continue + init_environment_vars.append(f"{match.group(1)}={match.group(2)}") + os.makedirs(CACHE_DIR, exist_ok=True) + with open(SERIAL_GETTY_FILE, "w", encoding="utf-8") as f: + f.write( + f"""[Service] +StandardInput=tty +StandardOutput=tty +TTYPath=/dev/%I +TTYReset=yes +TTYVHangup=yes +Environment={shlex.join(init_environment_vars)}""" + ) + if args.root == "/": + f.write(f"\nExecStart={guest_tools_path}/{virtme_init_cmd}") + else: + f.write(f"\nExecStart=/run/virtme/guesttools/{virtme_init_cmd}") qemuargs.extend(["-append", " ".join(quote_karg(a) for a in kernelargs)]) if initrdpath is not None: qemuargs.extend(["-initrd", initrdpath]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.36/virtme/guest/virtme-init new/virtme-ng-1.37/virtme/guest/virtme-init --- old/virtme-ng-1.36/virtme/guest/virtme-init 2025-05-11 16:37:00.000000000 +0200 +++ new/virtme-ng-1.37/virtme/guest/virtme-init 2025-08-08 09:40:53.000000000 +0200 @@ -19,8 +19,13 @@ mount -t proc -o nosuid,noexec,nodev proc /proc/ mount -t sysfs -o nosuid,noexec,nodev sys /sys/ -# Mount tmpfs dirs -mount -t tmpfs -o mode=0755 run /run/ +if [[ $$ -eq 1 ]]; then + # only mount /run if systemd is not the init + if ! grep -q "run /run" /proc/mounts; then + # Mount tmpfs dirs + mount -t tmpfs -o mode=0755 run /run/ + fi +fi mkdir /run/tmp # Setup rw filesystem overlays diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.36/virtme_ng/run.py new/virtme-ng-1.37/virtme_ng/run.py --- old/virtme-ng-1.36/virtme_ng/run.py 2025-05-11 16:37:00.000000000 +0200 +++ new/virtme-ng-1.37/virtme_ng/run.py 2025-08-08 09:40:53.000000000 +0200 @@ -29,7 +29,7 @@ from virtme.util import SilentError, get_username from virtme_ng.mainline import KernelDownloader -from virtme_ng.utils import CONF_FILE, spinner_decorator +from virtme_ng.utils import get_conf, spinner_decorator from virtme_ng.version import VERSION @@ -556,6 +556,12 @@ + "or to launch this command instead of a prompt (--client).", ) + parser.add_argument( + "--systemd", + action="store_true", + help="Execute systemd as init (EXPERIMENTAL)", + ) + return parser @@ -675,32 +681,9 @@ def __init__(self): self.virtme_param = {} - conf_path = self.get_conf_file_path() - self.default_opts = [] - if conf_path is not None: - with open(conf_path, encoding="utf-8") as conf_fd: - conf_data = json.loads(conf_fd.read()) - if "default_opts" in conf_data: - self.default_opts = conf_data["default_opts"] + self.default_opts = get_conf("default_opts") self.cpus = str(os.cpu_count()) - def get_conf_file_path(self): - """Return virtme-ng main configuration file path.""" - - # First check if there is a config file in the user's home config - # directory, then check for a single config file in ~/.virtme-ng.conf and - # finally check for /etc/virtme-ng.conf. If none of them exist, report an - # error and exit. - configs = ( - CONF_FILE, - Path(Path.home(), ".virtme-ng.conf"), - Path("/etc", "virtme-ng.conf"), - ) - for conf in configs: - if conf.exists(): - return conf - return None - def _format_cmd(self, cmd): return shlex.split(cmd) @@ -948,6 +931,12 @@ else: self.virtme_param["root"] = "" + def _get_virtme_systemd(self, args): + if args.systemd: + self.virtme_param["systemd"] = "--systemd" + else: + self.virtme_param["systemd"] = "" + def _get_virtme_rw(self, args): if args.rw: self.virtme_param["rw"] = "--rw" @@ -1301,6 +1290,7 @@ self._get_virtme_user(args) self._get_virtme_arch(args) self._get_virtme_root(args) + self._get_virtme_systemd(args) self._get_virtme_rw(args) self._get_virtme_rodir(args) self._get_virtme_rwdir(args) @@ -1349,6 +1339,7 @@ + f"{self.virtme_param['user']} " + f"{self.virtme_param['arch']} " + f"{self.virtme_param['root']} " + + f"{self.virtme_param['systemd']} " + f"{self.virtme_param['rw']} " + f"{self.virtme_param['rodir']} " + f"{self.virtme_param['rwdir']} " @@ -1397,38 +1388,72 @@ # Use QMP to generate a memory dump sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("localhost", 3636)) - data = sock.recv(1024) + sock_f = sock.makefile(encoding="utf-8") + data = sock_f.readline() if not data: + sys.stderr.write("Dump failed") sys.exit(1) if args.verbose: - sys.stdout.write(data.decode("utf-8")) - sock.send(b'{ "execute": "qmp_capabilities" }\r') - data = sock.recv(1024) + sys.stdout.write(data) + # Exit "QEMU capabilities negotiation mode" + sock.send(json.dumps({"execute": "qmp_capabilities"}).encode("utf-8")) + data = sock_f.readline() if not data: + sys.stderr.write("Dump failed") sys.exit(1) if args.verbose: - sys.stdout.write(data.decode("utf-8")) + sys.stdout.write(data) + if json.loads(data) != {"return": {}}: + sys.stderr.write(f"Dump failed:\n{data}") + sys.exit(1) dump_file = args.dump - with tempfile.NamedTemporaryFile(delete=dump_file is None) as tmp: - msg = ( - '{"execute":"dump-guest-memory",' - '"arguments":{"paging":true,' - '"protocol":"file:' + tmp.name + '"}}' - "\r" + with tempfile.NamedTemporaryFile( + delete=True, prefix="tmpvirtmedump_", dir=os.path.dirname(dump_file) + ) as tmp: + msg = json.dumps( + { + "execute": "dump-guest-memory", + "arguments": {"paging": True, "protocol": f"file:{tmp.name}"}, + } ) if args.verbose: sys.stdout.write(msg + "\n") sock.send(msg.encode("utf-8")) - data = sock.recv(1024) - if not data: - sys.exit(1) - if args.verbose: - sys.stdout.write(data.decode("utf-8")) - data = sock.recv(1024) - if args.verbose: - sys.stdout.write(data.decode("utf-8")) - # Save memory dump to target file - shutil.move(tmp.name, dump_file) + while True: + data = sock_f.readline() + if not data: + sys.stderr.write("Dump failed") + sys.exit(1) + if args.verbose: + sys.stdout.write(data) + try: + data_json = json.loads(data) + except json.decoder.JSONDecodeError: + sys.stderr.write(f"Dump failed:\n{data}") + sys.exit(1) + + # e.g. {"error": {"class": "GenericError", "desc": "Could not create 'bla.elf': Permission denied"}} + if "error" in data_json: + sys.stderr.write(f"Dump failed:\n{data}") + sys.exit(1) + + if data_json.get("event", "") != "DUMP_COMPLETED": + continue + + # Save memory dump to target file + shutil.move(tmp.name, dump_file) + + # e.g. {"timestamp": {"seconds": 1747057595, "microseconds": 633224}, "event": "DUMP_COMPLETED", "data": + # {"result": {"total": 1073741824, "status": "failed", "completed": 305700864}, "error": "dump: failed + # to save memory: No space left on device"}} + if "error" in data_json["data"]: + sys.stderr.write(f"Dump failed:\n{data}") + sys.exit(1) + + # We're done, e.g. {"timestamp": {"seconds": 1747057073, "microseconds": 930833}, "event": + # "DUMP_COMPLETED", "data": {"result": {"total": 1073741824, "status": "completed", "completed": + # 1073741824}}} + break def clean(self, args): """Clean a local or remote git repository.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.36/virtme_ng/utils.py new/virtme-ng-1.37/virtme_ng/utils.py --- old/virtme-ng-1.36/virtme_ng/utils.py 2025-05-11 16:37:00.000000000 +0200 +++ new/virtme-ng-1.37/virtme_ng/utils.py 2025-08-08 09:40:53.000000000 +0200 @@ -3,6 +3,7 @@ """virtme-ng: configuration path.""" +import json from pathlib import Path from virtme_ng.spinner import Spinner @@ -15,6 +16,18 @@ DEFAULT_VIRTME_SSH_HOSTNAME_CID_SEPARATOR = VIRTME_SSH_HOSTNAME_CID_SEPARATORS[0] CONF_PATH = Path(Path.home(), ".config", "virtme-ng") CONF_FILE = Path(CONF_PATH, "virtme-ng.conf") +SERIAL_GETTY_FILE = Path(CACHE_DIR, "serial-getty@.service") + +# NOTE: this must stay in sync with README.md +CONF_DEFAULT = { + "default_opts": {}, + "systemd": { + "masks": [ + # disable getty@, since we're forcing the use of serial-getty@ + "getty@" + ] + }, +} def spinner_decorator(message): @@ -27,3 +40,46 @@ return wrapper return decorator + + +def get_conf_obj(): + """Return virtme-ng main configuration, returning the default if not found.""" + + # First check if there is a config file in the user's home config + # directory, then check for a single config file in ~/.virtme-ng.conf and + # finally check for /etc/virtme-ng.conf. If none of them exist, return the + # default configuration. + conf_paths = ( + CONF_FILE, + Path(Path.home(), ".virtme-ng.conf"), + Path("/etc", "virtme-ng.conf"), + ) + for conf_path in conf_paths: + if conf_path.exists(): + with open(conf_path, encoding="utf-8") as conf_fd: + conf = json.loads(conf_fd.read()) + return conf + return CONF_DEFAULT + + +def get_conf(key_path): + """Return a configured value for a key_path, which might be nested + + >>> get_conf("default_opts") + {} + >>> get_conf("systemd") + {'masks': ["getty@"]} + >>> get_conf("systemd.masks") + ["getty@"] + """ + keys = key_path.split(".") + conf = get_conf_obj() + try: + for key in keys: + conf = conf[key] + return conf + except (KeyError, TypeError): + conf = CONF_DEFAULT + for key in keys: + conf = conf[key] + return conf diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.36/virtme_ng/version.py new/virtme-ng-1.37/virtme_ng/version.py --- old/virtme-ng-1.36/virtme_ng/version.py 2025-05-11 16:37:00.000000000 +0200 +++ new/virtme-ng-1.37/virtme_ng/version.py 2025-08-08 09:40:53.000000000 +0200 @@ -7,7 +7,7 @@ from importlib.metadata import PackageNotFoundError, version from subprocess import DEVNULL, CalledProcessError, check_output -PKG_VERSION = "1.36" +PKG_VERSION = "1.37" def get_package_version(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/virtme-ng-1.36/virtme_ng_init/src/main.rs new/virtme-ng-1.37/virtme_ng_init/src/main.rs --- old/virtme-ng-1.36/virtme_ng_init/src/main.rs 2025-05-11 16:37:00.000000000 +0200 +++ new/virtme-ng-1.37/virtme_ng_init/src/main.rs 2025-08-08 09:40:53.000000000 +0200 @@ -192,13 +192,6 @@ const USER_SCRIPT: &str = "/run/tmp/.virtme-script"; -fn check_init_pid() { - if id() != 1 { - log!("must be run as PID 1"); - exit(1); - } -} - fn poweroff() { unsafe { libc::sync(); @@ -413,6 +406,10 @@ // Note, get_test_tools_dir() relies on /proc, so that must be mounted // prior to /run. if mount_info.target == "/run" { + if id() != 1 { + // systemd is the current init, skip mounting /run + continue; + } if let Some(guest_tools_dir) = get_guest_tools_dir() { if guest_tools_dir.starts_with("/run") { log!("/run previously mounted, skipping"); @@ -1106,9 +1103,6 @@ } fn main() { - // Make sure to always run as PID 1. - check_init_pid(); - // Basic system initialization (order is important here). configure_environment(); configure_hostname();