clan-munix/munix
Val Packett 604ebc1356 [BREAKING] Switch to virtgpu-based D-Bus tunneling
This introduces support for drag&drop and printing portals, and later
camera/screencasting should be possible as well. However we break
backwards compatibility with already built closures because the
nixosModule needs to be changed.

In the next commit, the runtime environment related services will be
removed from the nixosModule to prevent unnecessary future breakage.
2026-03-06 05:09:55 -03:00

253 lines
8.3 KiB
Bash
Executable file

#!/usr/bin/env bash
SCRIPT_PATH=$(dirname $(realpath -s $0))
MUVM_PATH=$(dirname $(which muvm))
PASST_PATH=$(dirname $(which passt))
HOST_OPENGL_DRIVER=/run/opengl-driver
: "${MICROVM_DEFAULT_COMMAND:=bash}"
MICROVM_CLOSURE=
MICROVM_COMMAND=()
MICROVM_UID=1337
MICROVM_GID=1337
BWRAP_ARGS=()
MUVM_ARGS=()
SIDEBUS_ARGS=()
GPU=1
WAYLAND=1
PIPEWIRE=1
X11=0
ENV_DEFAULTS=1
USING_PUBLISH=0
export TMP=/tmp TMPDIR=/tmp TEMP=/tmp TEMPDIR=/tmp LC_ALL=C
if [ ! -e "$HOST_OPENGL_DRIVER" ]; then
HOST_OPENGL_DRIVER="$FALLBACK_OPENGL_DRIVER"
fi
while [ "$#" -gt 0 ]; do
case "$1" in
-u|--uid) MICROVM_UID="$2"; shift 2;;
-g|--gid) MICROVM_GID="$2"; shift 2;;
--no-gpu) GPU=0; shift 1;;
--no-wayland) WAYLAND=0; shift 1;;
--no-pipewire) PIPEWIRE=0; shift 1;;
--no-env-defaults) ENV_DEFAULTS=0; shift 1;;
--x11) X11=1; shift 1;;
-b|--bind) BWRAP_ARGS+=("--bind" "$2" "$3"); SIDEBUS_ARGS+=("--path-mapping" "$3=$2"); shift 3;;
--ro-bind) BWRAP_ARGS+=("--ro-bind" "$2" "$3"); SIDEBUS_ARGS+=("--path-mapping" "$3=$2"); shift 3;;
-e|--expose) BWRAP_ARGS+=("--bind" "$2" "$2"); SIDEBUS_ARGS+=("--path-mapping" "$2=$2"); shift 2;;
--ro-expose) BWRAP_ARGS+=("--ro-bind" "$2" "$2"); SIDEBUS_ARGS+=("--path-mapping" "$2=$2"); shift 2;;
-p|--publish) USING_PUBLISH=1; MUVM_ARGS+=("--publish=$2"); shift 2;;
--host-opengl-driver) HOST_OPENGL_DRIVER="$2"; shift 2;;
--munix-bin-dir) SCRIPT_PATH="$2"; shift 2;;
--muvm-bin-dir) MUVM_PATH="$2"; shift 2;;
--passt-bin-dir) PASST_PATH="$2"; shift 2;;
--) shift 1; MICROVM_COMMAND+=("$@"); break;;
-*) echo "munix: unknown option: $1" >&2; exit 1;;
*)
if [ "$MICROVM_CLOSURE" = "" ]; then
MICROVM_CLOSURE="$1"
else
MICROVM_COMMAND+=("$1")
fi
shift 1;;
esac
done
if [ "$MICROVM_CLOSURE" = "" ]; then
echo "munix: provide a system closure path as a positional argument" >&2
exit 1
fi
if [ "$MUVM_PATH" = "" ]; then
echo "munix: muvm not found, provide a --muvm-bin-dir or fix \$PATH" >&2
exit 1
fi
if [ "$PASST_PATH" = "" ]; then
echo "munix: passt not found, provide a --passt-bin-dir or fix \$PATH" >&2
exit 1
fi
if [ ! -e "$HOST_OPENGL_DRIVER" ]; then
echo "munix: host graphics driver not found, provide a --host-opengl-driver" >&2
exit 1
fi
# Resolve symlinks automatically
MICROVM_CLOSURE=$(realpath "$MICROVM_CLOSURE")
if [ ${#MICROVM_COMMAND[@]} -eq 0 ]; then
MICROVM_COMMAND=("$MICROVM_DEFAULT_COMMAND")
fi
if [ "$GPU" -eq 1 ]; then
BWRAP_ARGS+=(
"--dev-bind" "/dev/dri" "/dev/dri"
"--ro-bind" "$HOST_OPENGL_DRIVER" "/run/opengl-driver"
)
GPU_MODE=venus
kernel_ver="$(uname -r)"
kernel_ver_arr=(${kernel_ver//./ })
kernel_major="${kernel_ver_arr:-0}"
kernel_ver_arr=("${kernel_ver_arr[@]:1}")
kernel_minor="${kernel_ver_arr:-0}"
if [[ "$kernel_major" -gt 6 || ("$kernel_major" -eq 6 && "$kernel_minor" -gt 12) ]]; then
for card in /dev/dri/card*; do
driver_link="/sys/class/drm/${card##*/}/device/driver"
if [ -L "$driver_link" ]; then
driver_mod="$(readlink "$driver_link")"
driver_name="${driver_mod##*/}"
case "$driver_name" in
amdgpu|msm_dpu) # TODO: i915
echo "munix: ${card##*/} gpu driver is '$driver_name', using vdrm" >&2;
GPU_MODE=drm
break;;
*) echo "munix: ${card##*/} gpu driver is '$driver_name', using venus unless more gpus are found" >&2;;
esac
else
echo "munix: ${card##*/} has no gpu driver" >&2;
fi
done
else
echo "munix: kernel version '$kernel_ver' is older than 6.13, not using gpu due to missing support" >&2;
GPU_MODE=software
fi
MUVM_ARGS+=("--gpu-mode=$GPU_MODE")
else
BWRAP_ARGS+=("--dir" "/dev/dri")
MUVM_ARGS+=("--gpu-mode=software")
fi
if [ "$WAYLAND" -eq 1 ]; then
if [ "$XDG_RUNTIME_DIR" = "" ]; then
echo "munix: wayland requested, but no XDG_RUNTIME_DIR set" >&2
exit 1
fi
BWRAP_ARGS+=(
"--bind" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"
"--setenv" "WAYLAND_DISPLAY" "$WAYLAND_DISPLAY"
)
if [ "$ENV_DEFAULTS" -eq 1 ]; then
MUVM_ARGS+=(
"-e" "XDG_SESSION_TYPE=wayland"
"-e" "SDL_VIDEODRIVER=wayland"
"-e" "QT_QPA_PLATFORM=wayland"
"-e" "_JAVA_AWT_WM_NONREPARENTING=1" # e.g. with xwayland-satellite
"-e" "ELECTRON_OZONE_PLATFORM_HINT=wayland" # 28 < Electron < 39; newer should work by default
)
fi
fi
if [ "$PIPEWIRE" -eq 1 ]; then
if [ "$PIPEWIRE_RUNTIME_DIR" = "" ]; then
PIPEWIRE_RUNTIME_DIR="$XDG_RUNTIME_DIR"
fi
if [ "$PIPEWIRE_RUNTIME_DIR" = "" ]; then
PIPEWIRE_RUNTIME_DIR="$USERPROFILE"
fi
if [ "$PIPEWIRE_RUNTIME_DIR" = "" ]; then
echo "munix: pipewire requested, but no PIPEWIRE_RUNTIME_DIR/XDG_RUNTIME_DIR/USERPROFILE set" >&2
exit 1
fi
if [ "$PIPEWIRE_REMOTE" = "" ]; then
PIPEWIRE_REMOTE=pipewire-0
fi
BWRAP_ARGS+=("--bind" "$PIPEWIRE_RUNTIME_DIR/$PIPEWIRE_REMOTE" "$PIPEWIRE_RUNTIME_DIR/$PIPEWIRE_REMOTE")
if [ "$ENV_DEFAULTS" -eq 1 ]; then
MUVM_ARGS+=(
"-e" "SDL_AUDIO_DRIVER=pipewire"
)
fi
fi
if [ "$X11" -eq 1 ]; then
BWRAP_ARGS+=(
"--bind" "/tmp/.X11-unix" "/tmp/.X11-unix"
"--ro-bind" "$XAUTHORITY" "$XAUTHORITY"
)
else
unset DISPLAY XAUTHORITY
fi
declare -a BG_PIDS
cleanup() {
for pid in "${BG_PIDS[@]}"; do
if kill -0 "$pid" 2>/dev/null; then
echo "Killing process $pid"
kill -INT "$pid"
wait "$pid" 2>/dev/null
fi
done
exit
}
trap cleanup EXIT INT TERM
HOST_RUNTIME_DIR="$XDG_RUNTIME_DIR/munix.$$"
mkdir -p $HOST_RUNTIME_DIR
rm $HOST_RUNTIME_DIR/*
mkdir -p "$HOST_RUNTIME_DIR/home"
RUST_LOG=debug sidebus-broker \
--path-mapping "/home=$HOST_RUNTIME_DIR/home" \
"${SIDEBUS_ARGS[@]}" \
--guest-mountpoint /mnt/munix-doc-portal/doc \
--runtime-dir "$HOST_RUNTIME_DIR" \
--unix-path "$HOST_RUNTIME_DIR/port.sock" & # >/dev/null 2>&1 &
BG_PIDS+=("$!")
while [ ! -S "$HOST_RUNTIME_DIR/port.sock" ]; do sleep 0.1; done
BWRAP_ARGS=( # prepend home mount before other mounts to not override custom bind mounts under /home
--bind "$HOST_RUNTIME_DIR/home" /home
"${BWRAP_ARGS[@]}"
--bind "$HOST_RUNTIME_DIR" /mnt/munix-doc-portal
--setenv "RUTABAGA_DBUS_CLIENT_SOCKET" /mnt/munix-doc-portal/port.sock
)
if [ "$ENV_DEFAULTS" -eq 1 ]; then
MUVM_ARGS+=(
"-e" "GTK_USE_PORTAL=1" # GTK 3 including Firefox
"-e" "QT_QPA_PLATFORMTHEME=xdgdesktopportal"
)
fi
if [ "$USING_PUBLISH" -eq 1 ]; then
printf "\n\n\n\e[1mNote: due to a \e[31mBUG\e[39m with port publishing (-p) you have to send the first outgoing packet (e.g. ping -c1 8.8.8.8) before your ports start receiving traffic. Sorry for the inconvenience!\e[39;0m\n\n\n\n" >&2
fi
# xxx: some time is lost to the starting process..
[[ "$(</proc/uptime)" =~ ([0-9]+)\.([0-9]+) ]]
BOOT_TIME_OFFSET="${BASH_REMATCH[1]} $(( ${BASH_REMATCH[2]} * 1000000 ))"
# do not 'exec' because of cleanup :)
bwrap --unshare-all --share-net \
--uid $MICROVM_UID --gid $MICROVM_GID \
--tmpfs / \
--dir /run --dir /var --symlink /run /var/run --dir /tmp --dir /mnt --dir /bin --dir /usr/bin \
--proc /proc --ro-bind /sys /sys \
--dev /dev --dir /dev/input --dev-bind /dev/kvm /dev/kvm \
--ro-bind "$MUVM_PATH" /run/munix/muvm \
--ro-bind "$PASST_PATH" /run/munix/passt \
--ro-bind "$SCRIPT_PATH/micro-activate" /opt/bin/micro-activate \
--ro-bind "$MUVM_PATH/muvm-guest" /opt/bin/muvm-remote \
--ro-bind "$MUVM_PATH/muvm-guest" /opt/bin/muvm-configure-network \
--ro-bind "$MUVM_PATH/muvm-guest" /opt/bin/muvm-pwbridge \
--ro-bind "$MUVM_PATH/muvm-guest" /opt/bin/muvm-dbusbridge \
--symlink "$MICROVM_CLOSURE/etc" /etc \
--symlink "$MICROVM_CLOSURE/sw/bin/sh" /bin/sh \
--symlink "$MICROVM_CLOSURE/sw/bin/env" /usr/bin/env \
--symlink "$MICROVM_CLOSURE" /run/current-system \
--ro-bind /nix/store /nix/store \
--file 13 /run/resolv.conf \
--file 14 /run/localtime \
--dir "$XDG_RUNTIME_DIR" \
--setenv PATH "/run/munix/muvm:/run/munix/passt:$MICROVM_CLOSURE/sw/bin" \
"${BWRAP_ARGS[@]}" \
muvm \
--custom-init-cmdline "/opt/bin/micro-activate $MICROVM_CLOSURE/sw/sbin/init --log-target=console" \
"${MUVM_ARGS[@]}" \
-e container=munix \
-e MICROVM_CLOSURE="$MICROVM_CLOSURE" \
-e MICROVM_UID="$MICROVM_UID" -e MICROVM_GID="$MICROVM_GID" \
-e BOOT_TIME_OFFSET="$BOOT_TIME_OFFSET" \
-i -t -- "${MICROVM_COMMAND[@]}" \
13< /etc/resolv.conf \
14< /etc/localtime