From: Martin Pitt Date: Sun, 29 Jun 2025 08:24:46 +0000 (+0200) Subject: build-debian-toolbox: Fix for Ubuntu containers X-Git-Url: https://piware.de/gitweb/?p=bin.git;a=commitdiff_plain;h=HEAD;hp=6ad9f22601e5cbe2be714f68b5caffe9e96920da build-debian-toolbox: Fix for Ubuntu containers toolbox trips over the existing ubuntu home dir and its uid 1000. --- diff --git a/backup b/backup index 7a42486..79dc369 100755 --- a/backup +++ b/backup @@ -1,8 +1,14 @@ #!/bin/sh set -eu cd $HOME -LOG=.cache/duplicity/log +LOG=.cache/backup/log PATH=$PATH:/sbin:/usr/sbin +RESTIC="restic --password-file $HOME/.config/backup-passphrase --repo sftp:piware.de:backup/restic" + +fail() { + notify-send -i /usr/share/icons/Adwaita/48x48/status/network-error-symbolic.symbolic.png -u critical -t 180000 "${1:-BACKUP FAILED!}" + exit 1 +} # do backup every day if [ -e "$LOG" ] && [ $(( `date +%s` - `stat -c %Y $LOG` )) -lt 86300 ]; then @@ -27,6 +33,18 @@ fi notify-send "Backup started" mkdir -p $(dirname $LOG) -env PASSPHRASE="$(cat ~/.backup-passphrase)" duplicity --allow-source-mismatch --full-if-older-than 1M --exclude-filelist .duplicity-ignore . rsync://piware.de/backup/laptop >> $LOG || { notify-send "BACKUP FAILED!"; exit 1; } +date >> $LOG + +$RESTIC backup --exclude-file=$HOME/.config/backup-ignore $HOME >> $LOG 2>&1 || fail +# TODO: forget --prune policy: https://restic.readthedocs.io/en/stable/060_forget.html notify-send "Backup finished successfully" -duplicity remove-all-but-n-full 6 --force rsync://piware.de/backup/laptop + +scp .config/backup-passphrase piware.de:.cache/ +ssh piware.de chmod u+w .cache/backup-passphrase +trap "ssh piware.de shred -u .cache/backup-passphrase" EXIT INT QUIT PIPE + +ssh piware.de restic --password-file backup/backup-passphrase --repo backup/restic forget --prune --keep-within-hourly 24h --keep-within-daily 7d --keep-within-weekly 30d --keep-within-monthly 12m +notify-send "Backup pruned successfully" + +ssh piware.de restic --password-file backup/backup-passphrase --repo backup/restic check || fail "BACKUP CHECK FAILED!" +notify-send "Backup checked successfully" diff --git a/build-cockpit-toolbox b/build-cockpit-toolbox index 3b0ae86..868a9d2 100755 --- a/build-cockpit-toolbox +++ b/build-cockpit-toolbox @@ -4,8 +4,8 @@ NAME=${NAME:-cockpit} TAG=${TAG:-latest} toolbox rm --force $NAME || true -podman pull quay.io/cockpit/tasks:${TAG} -yes | toolbox create --image quay.io/cockpit/tasks:${TAG} -c $NAME +podman pull ghcr.io/cockpit-project/tasks:${TAG} +yes | toolbox create --image ghcr.io/cockpit-project/tasks:${TAG} -c $NAME # install some extra development and desktop tools toolbox run -c "$NAME" sh -exc ' @@ -16,42 +16,17 @@ sudo hostname -F /etc/hostname # enable manpages sudo sed -i s/nodocs// /etc/dnf/dnf.conf -sudo dnf install -y ansible man-db man-pages moreutils fd-find ripgrep gh \ +sudo dnf install -y ansible bash-completion man-db man-pages moreutils fd-find ripgrep gh git-delta neovim \ python3-boto python3-boto3 python3-openstacksdk libnotify \ - pandoc texlive-ec texlive-pdfjam texlive-beamer \ - /usr/bin/scanimage /usr/bin/pngquant /usr/bin/convert \ - kpcli \ + simple-scan /usr/bin/scanimage /usr/bin/pngquant /usr/bin/convert pdfmerge \ + cargo rustfmt clippy \ calibre qt5-qtwayland \ - chromium dbus-daemon flatpak-builder flatpak-spawn - -# HACK: tmt-provision-virtual has way too heavy dependencies: https://bugzilla.redhat.com/show_bug.cgi?id=2093717 -cat < /tmp/spec -Name: dummy-deps -Summary: dummy dependencies -Version: 999 -Release: 1%{?dist} -# https://bugzilla.redhat.com/show_bug.cgi?id=2093717 -Provides: python3-libguestfs -Provides: libguestfs-tools-c -# also for tmt-provision-virtual -Provides: libvirt -License: BSD - -%description -Dummy dependencies to avoid bloat. - -%files -EOF -rpmbuild --define "_topdir /tmp/rpmbuild-dummy" -bb /tmp/spec -sudo rpm -i --verbose /tmp/rpmbuild-dummy/RPMS/x86_64/dummy-deps-999-1.fc*.x86_64.rpm -rm -rf /tmp/rpmbuild-dummy /tmp/spec - -sudo dnf install -y --setopt=install_weak_deps=False tmt tmt-provision-virtual - -# run podman in toolbox -printf "flatpak-spawn --host podman \42\$@\42\n" | sudo tee /usr/local/bin/podman >/dev/null -sudo chmod a+x /usr/local/bin/podman - -# debugging cockpit crashes -sudo dnf debuginfo-install -y glib2 glibc libssh gnutls + tmt python3-testcloud flatpak-builder flatpak-spawn qemu-ui-sdl \ + fedpkg centpkg + +# commands to forward to the host +for cmd in podman buildah toolbox nmcli eog evince flatpak; do + printf "#!/bin/sh\nexec flatpak-spawn --host $cmd \42\$@\42\n" | sudo tee /usr/local/bin/$cmd >/dev/null + sudo chmod a+x /usr/local/bin/$cmd +done ' diff --git a/build-debian-toolbox b/build-debian-toolbox index 8286876..df36cd0 100755 --- a/build-debian-toolbox +++ b/build-debian-toolbox @@ -8,6 +8,11 @@ toolbox rm -f $RELEASE || true podman pull docker.io/$DISTRO:$RELEASE toolbox -y create -c $RELEASE --image docker.io/$DISTRO:$RELEASE +# hack for ubuntu: need to remove existing home dir +if [ "$DISTRO" = "ubuntu" ]; then + podman unshare sh -exc "H=\$(podman mount $RELEASE); rm -r \$H/home/ubuntu; sed -i '/^ubuntu/d' \$H/etc/passwd \$H/etc/shadow" +fi + # can't do that with toolbox run yet, as we need to install sudo first podman start $RELEASE podman exec -it $RELEASE sh -exc ' @@ -38,13 +43,19 @@ toolbox run --container $RELEASE sh -exc ' echo "${ID}-${VERSION_ID:-sid}" | sudo tee /etc/hostname sudo hostname -F /etc/hostname -sudo eatmydata apt-get -y dist-upgrade +sudo DEBIAN_FRONTEND=noninteractive eatmydata apt-get -y dist-upgrade # development tools -sudo eatmydata apt-get install -y --no-install-recommends build-essential git-buildpackage libwww-perl less vim lintian debhelper manpages-dev git dput pristine-tar bash-completion wget gnupg ubuntu-dev-tools python3-debian fakeroot libdistro-info-perl openssh-client +sudo eatmydata apt-get install -y --no-install-recommends build-essential git-buildpackage libwww-perl less vim lintian debhelper manpages-dev git dput pristine-tar bash-completion wget gnupg ubuntu-dev-tools python3-debian fakeroot libdistro-info-perl openssh-client flatpak-xdg-utils # autopkgtest -sudo eatmydata apt-get install -y --no-install-recommends autopkgtest qemu-system-x86 qemu-utils genisoimage +sudo eatmydata apt-get install -y --no-install-recommends autopkgtest qemu-system-x86 qemu-utils genisoimage uidmap + +# commands to forward to the host +for cmd in podman buildah toolbox; do + printf "#!/bin/sh\n/usr/libexec/flatpak-xdg-utils/flatpak-spawn --host $cmd \42\$@\42\n" | sudo tee /usr/local/bin/$cmd >/dev/null + sudo chmod a+x /usr/local/bin/$cmd +done ' toolbox enter --container $RELEASE diff --git a/consors-report.py b/consors-report.py new file mode 100755 index 0000000..1241dbc --- /dev/null +++ b/consors-report.py @@ -0,0 +1,109 @@ +#!/usr/bin/python3 + +import argparse +import collections +import csv +import itertools +import re +from collections import namedtuple +from pathlib import Path +from typing import Iterable + +Entry = namedtuple('Entry', ['category', 'date', 'valuta', 'who', 'iban', 'bic', 'type', 'desc', 'value']) + +CATEGORIES = { + 'Gehalt/Steuern': re.compile('Gehalt/Rente|RED HAT|Finanzamt|FK Guenzburg', re.I), + 'Wohnung': re.compile('Telekom|hands4home|Goetzfried|Green Planet Energy|Beitragsservice.*Rundfunk', re.I), + 'Medizin': re.compile(r'Apotheke|MVZ|\bmed\b|ZAB Abrechnung|Caika|HNOeins|PVS|Dr\..*Sellier|' + r'BFS Health|Streifeneder|Labor|Physio|(Drescher.*Lung)|Osteopath|' + '(Dr.*Borchers)|(Debeka.*Überweisung)|(DKV.*Überweisung)|Beihilfe|Klinik|' + 'Niklas Hermann|Alena Hauser|Strizrep|Aerzte|Hallesche.*Ausz|Physico|' + 'Orthop|Pathologie', re.I), + 'Versicherung': re.compile('((debeka|DKV|Hallesche|Versicherung|Alte Leipziger|ConceptIF|' + 'Baloise).*Lastschrift)|Hallesche.*Bonu', re.I), + 'Transport': re.compile(r'DB Vertr\s*ieb|DB Fernverkehr|Deutsche Bahn|Nextbike|Carsharing|Radstation', re.I), + 'Lebensmittel': re.compile('BIOS|Bäcker|Baecker|Baker|Schubert Bio|Ruta|Rewe|Edeka', re.I), + 'Eigentumswohnungen': re.compile('Rechnung Darl.-Leistung|Semmelweis|BHP Buerkner|Dr. Schmitz', re.I), + 'Hobby/Sport': re.compile('Holstein|Mrs. Sporty|Kieser|DJK|Dimaso', re.I), + 'Sparen': re.compile('Bausparkasse Mainz AG|FIL Fondsbank|MIG (Fonds|GmbH)|Netfonds AG|Festgeld|' + 'MORGAN STANLEY', re.I), + 'Spenden': re.compile('Spende|Signal Foundation|nebenan.de|(paypal.*Sabine.Hossenf)|' + 'Patreon|Umwelthilfe|Foerderer|Malteser|(Aktion Tier.*Mensch)|' + 'campact|Amnesty|BN Landesverband', re.I), +} + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description='Read Consorsbank CSV and generate report') + parser.add_argument('-d', '--date', metavar='REGEX', default='.', help='date filter regex') + parser.add_argument('-s', '--summary', action='store_true', help='only show category sums') + parser.add_argument('csv', type=Path, nargs='+', help='CSV files to parse') + return parser.parse_args() + + +def get_category(item: str) -> str: + for category, pattern in CATEGORIES.items(): + if pattern.search(item): + return category + return 'Sonstiges' + + +def parse_entry(raw_fields: Iterable[str]) -> Entry: + fields = [f.strip() for f in raw_fields] + # format change in May 2024, adds a 9th field "Währung"; ignore + fields = fields[:8] + # last field is the value, parse as float + value = float(fields.pop().replace('.', '').replace(',', '.')) + # match on who, IBAN, type, or desc + category = get_category(fields[2] + fields[3] + fields[5] + fields[6]) + return Entry._make([category, *fields, value]) + + +def parse_csv(path: Path, date_filter: str) -> Iterable[Entry]: + filter_re = re.compile(date_filter, re.I) + + def filter_entry(entry: Entry): + # ignore from/to Tagesgeldkonto + if entry.iban == 'DE13760300800853589101': + return None + return filter_re.search(entry.date) + + with path.open() as f: + reader = csv.reader(f, delimiter=';') + next(reader) # skip header + # first line is the column headers, chop it off + entries = map(parse_entry, reader) + # do the actual iteration here, as the files get closed afterwards + return list(filter(filter_entry, entries)) + + +def main(): + args = parse_args() + + entries = itertools.chain.from_iterable(map(lambda p: parse_csv(p, args.date), args.csv)) + by_category = collections.defaultdict(lambda: []) + category_sum = collections.defaultdict(lambda: 0.0) + balance = 0.0 + for e in entries: + by_category[e.category].append(e) + category_sum[e.category] += e.value + balance += e.value + + if args.summary: + for cat in sorted(by_category): + print(f'| {category_sum[cat]:>8.2f} | {cat:20} |') + print('|----------|----------------------|') + print(f'| {balance:>8.2f} | {"Saldo":20} |') + + else: + for cat in sorted(by_category): + print(f'## {cat}: {category_sum[cat]:.2f}\n') + for entry in by_category[cat]: + print(f'| {entry.date:7} | {entry.value:>7.2f} | _{entry.type}_ - {entry.who} - {entry.desc} |') + print() + + print(f'## Saldo: {balance:.2f}') + + +if __name__ == '__main__': + main() diff --git a/daytime-term b/daytime-term index 102e417..51f9b82 100755 --- a/daytime-term +++ b/daytime-term @@ -1,4 +1,5 @@ #!/bin/sh +DATA=$HOME/.cache/sunrise.json set -eu if [ "${1:-}" = "night" ]; then @@ -6,8 +7,17 @@ if [ "${1:-}" = "night" ]; then night=1 elif [ "${1:-}" = "day" ]; then night= -elif [ `date +%k` -ge 20 ] || [ `date +%k` -lt 5 ]; then - night=1 +else + # get daylight times at most once a day + if [ ! -e "$DATA" ] || [ $(( `date +%s` - `stat -c %Y $DATA` )) -gt 86400 ]; then + curl --silent --show-error -o "$DATA" 'https://api.sunrise-sunset.org/json?lat=48.22&lng=10.54&formatted=0' + fi + sunrise=$(jq -r .results.sunrise < ~/.cache/sunrise.json) + sunset=$(jq -r .results.sunset < ~/.cache/sunrise.json) + now=$(date '+%H%M') + if [ $now -le $(date -d "$sunrise" '+%H%M') ] || [ $now -ge $(date -d "$sunset" '+%H%M') ]; then + night=1 + fi fi if [ -n "${night:-}" ]; then diff --git a/debian-backport-cockpit b/debian-backport-cockpit index 109f848..a0284ed 100755 --- a/debian-backport-cockpit +++ b/debian-backport-cockpit @@ -20,14 +20,14 @@ upload() { rm -r ${PACKAGE}-* } +rm -rf /tmp/backport mkdir /tmp/backport cd /tmp/backport apt-get source $PACKAGE cd ${PACKAGE}-* -# bullseye -series_ver bullseye-backports -[ ! -x debian/adjust-for-release ] || debian/adjust-for-release bullseye -dch --local ~bpo11+ --distribution bullseye-backports --force-distribution "No-change backport to Debian Bullseye" -dpkg-buildpackage -S -sd -nc -v$ver +# bookworm +series_ver bookworm-backports +dch --local ~bpo12+ --distribution bookworm-backports --force-distribution "No-change backport to Debian Bookworm" +debuild -S -sd -nc -v$ver upload diff --git a/gomuks b/gomuks new file mode 100755 index 0000000..ebe585d --- /dev/null +++ b/gomuks @@ -0,0 +1,3 @@ +#!/bin/sh +printf '\033]2;gomuks\a' +TERM=xterm ~/.local/bin/gomuks diff --git a/install-rhel-tools b/install-rhel-tools index 7953c72..4fc6d7b 100755 --- a/install-rhel-tools +++ b/install-rhel-tools @@ -2,23 +2,10 @@ # Install RHEL development tools into a toolbox set -eux -sudo curl -o /etc/pki/ca-trust/source/anchors/RH-IT-Root-CA.crt https://password.corp.redhat.com/RH-IT-Root-CA.crt +sudo curl -o /etc/pki/ca-trust/source/anchors/2015-RH-IT-Root-CA.crt https://certs.corp.redhat.com/certs/2015-IT-Root-CA.pem +sudo curl -o /etc/pki/ca-trust/source/anchors/2022-RH-IT-Root-CA.crt https://certs.corp.redhat.com/certs/2022-IT-Root-CA.pem + sudo update-ca-trust (cd /etc/yum.repos.d; sudo curl -O --location https://download.devel.redhat.com/rel-eng/RCMTOOLS/rcm-tools-fedora.repo) -sudo dnf install -y rhpkg - -# CentOS stream -sudo dnf copr enable -y james/centpkg -sudo dnf install -y centpkg streamkoji - -# coverity -sudo dnf copr enable -y copr.devel.redhat.com/kdudka/covscan -sudo dnf install -y covscan-client - -# 1minutetip: https://wiki.test.redhat.com/BaseOs/Projects/1minuteTIP -if mountpoint /mnt; then sudo umount /mnt; fi # HACK: rhts-test-env-5.0-2.fc32eng.noarch wants to fiddle with it -. /etc/os-release -sudo wget -O /etc/yum.repos.d/qa-tools.repo https://copr.devel.redhat.com/coprs/lpol/qa-tools/repo/fedora-36/lpol-qa-tools-fedora-${VERSION_ID}.repo -sudo wget -O /etc/yum.repos.d/beaker-client.repo http://download.lab.bos.redhat.com/beakerrepos/beaker-client-Fedora.repo -sudo dnf install -y qa-tools-workstation-1minutetip tmt-redhat-provision-minute +sudo dnf install -y rhel-packager diff --git a/install-tex b/install-tex new file mode 100755 index 0000000..62d2a6f --- /dev/null +++ b/install-tex @@ -0,0 +1,6 @@ +#!/bin/sh +if command -v apt; then + sudo apt install -y texlive-lang-german texlive-latex-base texlive-latex-extra +else + sudo dnf install texlive-german texlive-german texlive-a4wide texlive-dinbrief texlive-latex texlive-hyphen-german pandoc texlive-ec texlive-pdfjam texlive-beamer +fi diff --git a/process-photos b/process-photos index 14ee5b3..219edf3 100755 --- a/process-photos +++ b/process-photos @@ -1,7 +1,11 @@ #!/bin/sh set -eu -for f in *.jpg *.JPG; do +for f in *.JPG; do + mv "$f" "${f%JPG}jpg" +done + +for f in *.jpg; do echo "$f" chmod 644 "$f" info="$(exiv2 -Pnv "$f")" diff --git a/recipekeeper-split b/recipekeeper-split new file mode 100755 index 0000000..a56f52e --- /dev/null +++ b/recipekeeper-split @@ -0,0 +1,86 @@ +#!/usr/bin/python3 +import html +import os +import re +import shutil +import sys +import xml.etree.ElementTree as ET +import zipfile + +(recipe_zip, destdir) = sys.argv[1:] + +# init destdir +if os.path.exists(destdir): + shutil.rmtree(destdir) +os.makedirs(destdir) + +with zipfile.ZipFile(recipe_zip, 'r') as archive: + archive.extractall(path=destdir) + + +def safe_filename(name): + name = re.sub(r"[^\w\s-]", '', name).strip().replace(' ', '_') + return name[:40] + + +with open(os.path.join(destdir, "recipes.html"), "r") as f: + content = f.read() + +# 1. Fix tags: make them self-closing +content_fixed = re.sub(r'(]*)(?', r'\1 />', content) + +# 2. Fix unquoted itemprop attribute values (itemprop=foo -> itemprop="foo") +# Only match when the value is not already quoted +content_fixed = re.sub(r'itemprop=([^\s">]+)', r'itemprop="\1"', content_fixed) + +# Parse as XML +tree = ET.ElementTree(ET.fromstring(content_fixed)) +root = tree.getroot() + +head = root.find('head') +body = root.find('body') +assert head is not None +assert body is not None +recipes = [div for div in body.findall('div') if div.get('class') == 'recipe-details'] + +# Prepare the tag string +meta_tag = '' + +for recipe in recipes: + h2 = recipe.find('.//h2[@itemprop="name"]') + meta_id = recipe.find('.//meta[@itemprop="recipeId"]') + if h2 is not None and h2.text and h2.text.strip(): + base = safe_filename(html.unescape(h2.text)) + elif meta_id is not None: + base = meta_id.attrib['content'] + else: + base = "recipe" + + filename = f"{base}.html" + + # Convert head to string and insert the meta tag after + head_str = ET.tostring(head, encoding="unicode") + head_str = re.sub( + r'()', # Match opening tag (with possible attributes) + r'\1\n' + meta_tag, # Insert meta tag right after opening tag + head_str, + count=1, + flags=re.IGNORECASE | re.DOTALL + ) + # fix too small font sizes + head_str = re.sub(r'14px', '18pt', head_str) + head_str = re.sub(r'24px', '24pt', head_str) + + out_html = ( + '\n\n' + + head_str + '\n' + + '\n' + + ET.tostring(recipe, encoding="unicode") + '\n' + + '\n' + ) + with open(os.path.join(destdir, filename), "w", encoding="utf-8") as out: + out.write(out_html) + + print(f"Wrote: {filename}") + +os.remove(os.path.join(destdir, "recipes.html")) diff --git a/scandoc b/scandoc index f62373c..3844e5c 100755 --- a/scandoc +++ b/scandoc @@ -13,15 +13,20 @@ echo "Output file directory: $D" PAGE=0 while true; do PAGE=$((PAGE+1)) - scanimage --device-name=genesys --format=png --progress --resolution 150 --mode Gray -x 210 -y 297 | convert -rotate 180 -modulate 120 -level 30,60% - cur.png + scanimage --device-name=genesys --format=png --progress --resolution 150 --mode Gray -x 210 -y 297 | magick - -rotate 180 -modulate 120 -level 30,60% cur.png FNAME=page`printf '%02i' $PAGE` pngquant 4 < cur.png > $FNAME.png - convert $FNAME.png $FNAME.pdf + magick $FNAME.png $FNAME.pdf echo "Insert next page and press Enter; q to stop" read x [ "$x" = q ] && break || true done -pdfjam --outfile "$name" --fitpaper true --rotateoversize false page*.pdf +# pdfmerge does not get along with just one input file +if [ $PAGE -eq 1 ]; then + mv page*.pdf "$name" +else + pdfmerge page*.pdf "$name" +fi rm -rf "$D" diff --git a/system-maintenance b/system-maintenance index 8db4659..4b3557c 100755 --- a/system-maintenance +++ b/system-maintenance @@ -11,6 +11,6 @@ rpm-ostree upgrade # fix fonts between Fedora releases rm -rf ~martin/.cache/fontconfig -su -c 'flatpak -y update' martin - fstrim -av + +echo 'Run this now: flatpak -y update' diff --git a/ubuntu-backport-cockpit b/ubuntu-backport-cockpit index a8cf5f4..b1d1828 100755 --- a/ubuntu-backport-cockpit +++ b/ubuntu-backport-cockpit @@ -1,9 +1,11 @@ #!/bin/sh set -eu PACKAGE=${1:-cockpit} -TARGET=${2:-jammy} +TARGET=${2:-noble} POCKET="${TARGET}-backports" +[ -z "${3:-}" ] || DSC=$(realpath "$3") + series_ver() { ver=$(wget -q -O- http://de.archive.ubuntu.com/ubuntu/dists/$1/universe/source/Sources.xz | xz -d | grep-dctrl -n -sVersion -XP $PACKAGE | sort -u | tail -n1) ver=${ver%~bpo*} @@ -25,17 +27,20 @@ upload() { rm -r ${PACKAGE}-* } +rm -rf /tmp/backport mkdir /tmp/backport cd /tmp/backport -apt-get source $PACKAGE +if [ -n "${DSC:-}" ]; then + dpkg-source -x "$DSC" +else + apt-get source $PACKAGE +fi cd ${PACKAGE}-* case "$TARGET" in - lunar) VER=23.04 ;; - kinetic) VER=22.10 ;; + oracular) VER=24.10 ;; + noble) VER=24.04 ;; jammy) VER=22.04 ;; - impish) VER=21.10 ;; - focal) VER=20.04 ;; *) echo "Unknown target $TARGET" >&2; exit 1 ;; esac diff --git a/vm b/vm index 174aedf..f403cb2 100755 --- a/vm +++ b/vm @@ -9,7 +9,7 @@ until echo '' | nc localhost $ssh_port 2>&1 >/dev/null | grep -q 'Connection ref ssh_port=$((ssh_port+1)) done echo "Host ssh port: $ssh_port" -qemu-system-x86_64 -enable-kvm -display sdl -m 2048 -device virtio-rng-pci \ +qemu-system-x86_64 -enable-kvm -cpu host -display sdl -m 2048 -device virtio-rng-pci \ -drive file="$image",if=virtio \ -virtfs local,id=src,path=$HOME,security_model=none,mount_tag=home,readonly=on \ -net nic,model=virtio -net user,hostfwd=tcp::${ssh_port}-:22 "$@"