mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 17:25:19 +03:00
feat: resident guest init (#333)
Get this thing going..
<!-- greptile_comment -->
<!-- greptile_summary -->
<h2><a
href="https://app.greptile.com/api/retrigger?id=63134761"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/RetriggerDark.svg?v=1"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/Retrigger.svg?v=1"><img
alt="Retrigger"
src="https://greptile-static-assets.s3.amazonaws.com/badges/Retrigger.svg?v=1"
align="right"></picture></a>Confidence Score: 5/5</h2>
The PR appears safe to merge; all previous findings are resolved and the
latest readiness change introduces no established actionable regression.
<h3>Summary</h3>
- Establishes required guest filesystems, runtime directories, device
permissions, and service processes.
- Reports initialization and service deaths over the lifecycle channel.
- Supports launch, restart, and shutdown commands for a resident guest.
- Separates service and workload identities and configures per-launch
runtime environments.
- Removes the currently inactive nescope screenshot option and makes
capture-chain verification fail explicitly when compositor readback is
unavailable.
- Reworks the guest image around `nesinit` as PID 1 without a
distribution service manager.
<h3>Diagram</h3>
```mermaid
sequenceDiagram
participant Host
participant Init as nesinit
participant FS as Guest filesystems
participant Services as Service stack
participant Workload
Init->>Host: Ready(protocol version)
Host->>Init: Boot(mount descriptors)
Init->>FS: Establish and mount shares
Init->>Services: Spawn services in order
Services-->>Init: Required sockets ready
Init->>Host: Initialized(service names)
Host->>Init: Launch(id, exec, on_exit)
Init->>Workload: Spawn with isolated UID/runtime
Init->>Host: Started(id)
Workload-->>Init: Exit status
Init->>Host: WorkloadExited(id, status)
Host->>Init: Launch / Restart / Shutdown
```
<sub>Reviews (4) · Last reviewed commit: ["fix(nesinit): readiness is a
socket
that..."](731d34df9d)</sub>
<!-- /greptile_comment -->
---------
Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
ec8b13d0c9
commit
8246aa5538
@@ -1,3 +0,0 @@
|
||||
baud="115200"
|
||||
term_type="vt100"
|
||||
agetty_options="--autologin root --noclear"
|
||||
@@ -1,3 +0,0 @@
|
||||
baud="115200"
|
||||
term_type="vt100"
|
||||
agetty_options="--noclear"
|
||||
@@ -1,16 +0,0 @@
|
||||
# Configuration for nescope, the headless compositor.
|
||||
#
|
||||
# Sources nestri-user-env rather than symlinking it, for the same reason
|
||||
# neshub and neswire do.
|
||||
. /etc/conf.d/nestri-user-env
|
||||
|
||||
RUST_LOG="${RUST_LOG:-nescope=info}"
|
||||
export RUST_LOG
|
||||
|
||||
# Started in plain-compositor mode by /etc/init.d/nescope (no command after
|
||||
# `--`): it comes up and waits for something to connect rather than wrapping
|
||||
# a payload. Starting an actual payload — a game, a desktop — is nesinit's
|
||||
# job, and nesinit is not open code yet. See build/README.md.
|
||||
NESCOPE_SOCKET="${NESCOPE_SOCKET:-nescope-0}"
|
||||
NESCOPE_INPUT_IPC="${NESCOPE_INPUT_IPC:-/tmp/nestri-input.sock}"
|
||||
export NESCOPE_SOCKET NESCOPE_INPUT_IPC
|
||||
@@ -1,18 +0,0 @@
|
||||
# Configuration for neshub, the media hub.
|
||||
#
|
||||
# Not a symlink to nestri-user-env like pipewire and friends: neshub wants a
|
||||
# setting of its own, so it sources that file rather than replacing it — same
|
||||
# pattern as nescope and neswire below.
|
||||
. /etc/conf.d/nestri-user-env
|
||||
|
||||
# What neshub logs, and nothing else.
|
||||
#
|
||||
# Scoped and exported for the reason spelled out at length in the old
|
||||
# nestri-guest-hub conf.d this replaces: an unscoped `RUST_LOG=info` turns on
|
||||
# every crate linked in, iroh included, and OpenRC hands a service its own
|
||||
# environment, not the shell's locals — an unexported variable here goes
|
||||
# nowhere.
|
||||
#
|
||||
# Overridable, so a bad boot can be re-run with more without a rebuild.
|
||||
RUST_LOG="${RUST_LOG:-neshub=info}"
|
||||
export RUST_LOG
|
||||
@@ -1,34 +0,0 @@
|
||||
HOME="/home/nestri"
|
||||
USER="nestri"
|
||||
LOGNAME="nestri"
|
||||
XDG_RUNTIME_DIR="/run/user/1000"
|
||||
XDG_CONFIG_HOME="/home/nestri/.config"
|
||||
XDG_DATA_HOME="/home/nestri/.local/share"
|
||||
XDG_CACHE_HOME="/home/nestri/.cache"
|
||||
XDG_STATE_HOME="/home/nestri/.local/state"
|
||||
XDG_DATA_DIRS="/usr/local/share:/usr/share"
|
||||
XDG_SESSION_TYPE="wayland"
|
||||
XDG_SESSION_DESKTOP="nestri"
|
||||
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus"
|
||||
|
||||
# Driver forcing — currently off, in both copies. Kept, commented, because
|
||||
# the reasoning still applies if it is ever needed again: capture happens
|
||||
# through a Vulkan layer, so a process reaching the GPU via native OpenGL
|
||||
# would render fine and capture nothing, and zink is what makes such a
|
||||
# process capturable at all. Nothing has needed it so far.
|
||||
#
|
||||
# /etc/profile.d/nestri-env.sh carries the same block, also commented, and
|
||||
# only runs for login shells — nothing here is one, every service below runs
|
||||
# from OpenRC. If these are ever re-enabled, re-enable both: one copy on and
|
||||
# one off means services and shells reach the GPU by different paths.
|
||||
#__GLX_VENDOR_LIBRARY_NAME="mesa"
|
||||
#MESA_LOADER_DRIVER_OVERRIDE="zink"
|
||||
#GALLIUM_DRIVER="zink"
|
||||
|
||||
nestri_export_env() {
|
||||
export HOME USER LOGNAME
|
||||
export XDG_RUNTIME_DIR XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME XDG_STATE_HOME
|
||||
export XDG_DATA_DIRS XDG_SESSION_TYPE XDG_SESSION_DESKTOP
|
||||
export DBUS_SESSION_BUS_ADDRESS
|
||||
#export __GLX_VENDOR_LIBRARY_NAME MESA_LOADER_DRIVER_OVERRIDE GALLIUM_DRIVER
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
# Configuration for the audio wire.
|
||||
#
|
||||
# Not a symlink to nestri-user-env like pipewire and friends, for the same
|
||||
# reason neshub is not: it needs a setting of its own -- and it still wants
|
||||
# that file's environment, so it sources it rather than replacing it.
|
||||
. /etc/conf.d/nestri-user-env
|
||||
|
||||
# What neswire logs, and nothing else.
|
||||
#
|
||||
# Without this neswire is *silent* -- it starts, fails, and exits leaving an
|
||||
# empty log, which is indistinguishable from never having been started at
|
||||
# all. That ambiguity is the reason this file exists.
|
||||
#
|
||||
# Scoped, and `export`ed, for the reasons spelled out at length in
|
||||
# conf.d/neshub: an unscoped `info` turns on every linked crate, and an
|
||||
# unexported one reaches the service's shell and not the service.
|
||||
#
|
||||
# Overridable, so a bad boot can be re-run with more without a rebuild.
|
||||
RUST_LOG="${RUST_LOG:-neswire=info}"
|
||||
export RUST_LOG
|
||||
|
||||
# neswire's own defaults, spelled out rather than inherited.
|
||||
#
|
||||
# All four are clap args with defaults (`neswire --help`), so the service
|
||||
# works without them. They are here because the IPC path is a contract with
|
||||
# neshub -- it reads this socket -- and a contract that lives only inside two
|
||||
# binaries' default values is one nobody can check.
|
||||
NESWIRE_IPC_PATH="/tmp/nestri-audio.sock"
|
||||
NESWIRE_CHANNELS="2"
|
||||
NESWIRE_PACKET_DURATION_MS="5"
|
||||
NESWIRE_BITRATE_PER_CHANNEL="64"
|
||||
export NESWIRE_IPC_PATH NESWIRE_CHANNELS NESWIRE_PACKET_DURATION_MS NESWIRE_BITRATE_PER_CHANNEL
|
||||
@@ -1,35 +0,0 @@
|
||||
# `ro`, matching how a box is actually started: nesbox passes `ro` on the
|
||||
# kernel command line and marks the root device `is_read_only: true` (see
|
||||
# nesbox's examples/vm.json), so the virtio-blk device refuses writes at the
|
||||
# device level. Saying `rw` here does not make it writable — it only asks
|
||||
# OpenRC's `root` service to attempt a remount that the device must reject.
|
||||
# Everything a running box writes to is a tmpfs or a share below.
|
||||
/dev/vda / ext4 ro,relatime 0 1
|
||||
devtmpfs /dev devtmpfs rw,nosuid 0 0
|
||||
proc /proc proc rw,nosuid,nodev,noexec 0 0
|
||||
sysfs /sys sysfs rw,nosuid,nodev,noexec 0 0
|
||||
tmpfs /tmp tmpfs rw,nosuid,nodev,size=64M 0 0
|
||||
tmpfs /run tmpfs rw,nosuid,nodev,size=32M,mode=0755 0 0
|
||||
tmpfs /var/log tmpfs rw,nosuid,nodev,size=16M 0 0
|
||||
# POSIX shared memory. devtmpfs does not provide it and nothing else mounts it,
|
||||
# so without this entry /dev/shm does not exist at all and `shm_open` fails.
|
||||
#
|
||||
# PipeWire itself gets by: it allocates buffers with memfd_create and only
|
||||
# falls back to /dev/shm. Kept as a precaution for anything else that uses
|
||||
# POSIX shm/semaphores directly — Proton is the known example, and it is not
|
||||
# part of this image yet (see build/README.md), but a failure here is silent
|
||||
# rather than fatal, so it costs nothing to have ready.
|
||||
#
|
||||
# `nosuid,nodev` and a size cap because everything a game can write to should
|
||||
# have both.
|
||||
tmpfs /dev/shm tmpfs rw,nosuid,nodev,size=256M 0 0
|
||||
# The guest's logs, on a host directory that outlives the VM.
|
||||
#
|
||||
# Here rather than mounted by a service, because the failures worth reading
|
||||
# are the ones that happen *before* anything else has mounted something — a
|
||||
# log directory that appears only after a successful start cannot record an
|
||||
# unsuccessful one. The tag is fixed by nessh, so plain fstab works.
|
||||
#
|
||||
# `nofail` because a VM started without the share still has to boot: that is
|
||||
# how somebody gets a shell to find out why it has no share.
|
||||
logs /nestri/logs virtiofs rw,nofail 0 0
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="D-Bus session bus for nestri user"
|
||||
|
||||
nestri_export_env
|
||||
|
||||
command="/usr/bin/dbus-daemon"
|
||||
command_args="--session --address=unix:path=/run/user/1000/bus --nofork --nopidfile --print-address"
|
||||
command_user="nestri:nestri"
|
||||
command_background="yes"
|
||||
pidfile="/run/nestri/dbus-session.pid"
|
||||
|
||||
depend() {
|
||||
need xdg-runtime dbus
|
||||
before pipewire wireplumber
|
||||
}
|
||||
|
||||
start_pre() {
|
||||
checkpath -d -m 0755 -o "nestri:nestri" /run/nestri
|
||||
if [ -e "/run/user/1000/bus" ]; then
|
||||
ewarn "Removing stale bus socket"
|
||||
rm -f "/run/user/1000/bus"
|
||||
fi
|
||||
}
|
||||
|
||||
start_post() {
|
||||
local i=0
|
||||
while [ ! -S /run/user/1000/bus ] && [ $i -lt 20 ]; do
|
||||
sleep 0.1
|
||||
i=$((i+1))
|
||||
done
|
||||
[ -S /run/user/1000/bus ] || { eerror "Bus didn't appear"; return 1; }
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="D-Bus system message bus"
|
||||
|
||||
command="/usr/bin/dbus-daemon"
|
||||
command_args="--system --nofork --nopidfile"
|
||||
command_background="yes"
|
||||
pidfile="/run/dbus/dbus.pid"
|
||||
|
||||
depend() {
|
||||
need localmount xdg-runtime
|
||||
before pipewire
|
||||
}
|
||||
|
||||
start_pre() {
|
||||
checkpath -d -m 0755 -o root:root /run/dbus
|
||||
checkpath -d -m 0755 -o messagebus:messagebus /var/run/dbus 2>/dev/null || true
|
||||
dbus-uuidgen --ensure=/var/lib/dbus/machine-id
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="Configure the guest's network to match the host's tap"
|
||||
|
||||
# Overridden from /etc/conf.d/guest-net if present. These defaults match
|
||||
# nesbox's own defaults; if you change the `network` section in the VM's
|
||||
# JSON, change these to match.
|
||||
: ${GUEST_IFACE:=eth0}
|
||||
: ${GUEST_IP:=172.30.0.2}
|
||||
: ${GUEST_PREFIX:=24}
|
||||
: ${GUEST_GATEWAY:=172.30.0.1}
|
||||
|
||||
depend() {
|
||||
need localmount
|
||||
provide net
|
||||
keyword -shutdown
|
||||
}
|
||||
|
||||
# Read one `nestri.<key>=<value>` from the kernel command line.
|
||||
#
|
||||
# The address has to come from somewhere per-boot, because the alternative --
|
||||
# baking it into the image -- makes every guest built from that image the
|
||||
# same host on the network. Two sandboxes then collide the moment they run
|
||||
# together.
|
||||
#
|
||||
# `nestri.`-prefixed rather than the kernel's own `ip=`: that one needs
|
||||
# CONFIG_IP_PNP and exists to configure NFS root, and the prefix makes it
|
||||
# obvious whose parameter this is.
|
||||
cmdline_value() {
|
||||
local key="$1" word
|
||||
for word in $(cat /proc/cmdline 2>/dev/null); do
|
||||
case "$word" in
|
||||
"nestri.${key}="*) printf '%s' "${word#nestri.${key}=}"; return 0 ;;
|
||||
esac
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
start() {
|
||||
ebegin "Bringing up loopback"
|
||||
ip link set lo up
|
||||
eend $?
|
||||
|
||||
# The command line wins over conf.d when it says anything, and conf.d is
|
||||
# the fallback so a hand-written VM config with no parameters keeps
|
||||
# working -- which is how a guest gets debugged.
|
||||
local source="/etc/conf.d/guest-net"
|
||||
local cmdline_ip
|
||||
if cmdline_ip="$(cmdline_value ip)"; then
|
||||
# Accepts address/prefix; a bare address keeps the configured prefix
|
||||
# rather than guessing one.
|
||||
case "$cmdline_ip" in
|
||||
*/*)
|
||||
GUEST_IP="${cmdline_ip%%/*}"
|
||||
GUEST_PREFIX="${cmdline_ip##*/}"
|
||||
;;
|
||||
*) GUEST_IP="$cmdline_ip" ;;
|
||||
esac
|
||||
source="kernel command line"
|
||||
fi
|
||||
|
||||
local cmdline_gw
|
||||
if cmdline_gw="$(cmdline_value gw)"; then
|
||||
GUEST_GATEWAY="$cmdline_gw"
|
||||
source="kernel command line"
|
||||
fi
|
||||
|
||||
# The VM may have been started with no network device at all, which is a
|
||||
# perfectly good configuration. Do not fail the boot over it.
|
||||
if [ ! -e "/sys/class/net/${GUEST_IFACE}" ]; then
|
||||
einfo "no ${GUEST_IFACE}: this VM has no network device"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Says which source won, because "the address is wrong" and "the address
|
||||
# came from somewhere unexpected" look identical from inside the guest.
|
||||
ebegin "Configuring ${GUEST_IFACE} as ${GUEST_IP}/${GUEST_PREFIX} via ${GUEST_GATEWAY} (from ${source})"
|
||||
# `replace` rather than `add` so a restart is not an error.
|
||||
ip link set "${GUEST_IFACE}" up &&
|
||||
ip addr replace "${GUEST_IP}/${GUEST_PREFIX}" dev "${GUEST_IFACE}" &&
|
||||
ip route replace default via "${GUEST_GATEWAY}" dev "${GUEST_IFACE}"
|
||||
eend $? "could not configure ${GUEST_IFACE}"
|
||||
}
|
||||
|
||||
stop() {
|
||||
if [ -e "/sys/class/net/${GUEST_IFACE}" ]; then
|
||||
ebegin "Bringing down ${GUEST_IFACE}"
|
||||
ip link set "${GUEST_IFACE}" down
|
||||
eend 0
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="Nestri headless compositor"
|
||||
|
||||
nestri_export_env
|
||||
|
||||
: "${NESTRI_UID:=1000}"
|
||||
: "${NESTRI_USER:=nestri}"
|
||||
|
||||
command="/usr/bin/nescope"
|
||||
# No command after `--`: plain-compositor mode. nescope comes up and waits
|
||||
# for something to connect rather than wrapping a payload — starting one is
|
||||
# nesinit's job, and nesinit is not open code yet. See build/README.md.
|
||||
command_user="${NESTRI_USER}:${NESTRI_USER}"
|
||||
command_background="yes"
|
||||
pidfile="/run/nestri/nescope.pid"
|
||||
output_log="/nestri/logs/nescope.log"
|
||||
error_log="/nestri/logs/nescope.log"
|
||||
respawn="yes"
|
||||
respawn_delay="2"
|
||||
respawn_max="2"
|
||||
|
||||
export XDG_RUNTIME_DIR="/run/user/${NESTRI_UID}"
|
||||
|
||||
depend() {
|
||||
need xdg-runtime
|
||||
use neshub
|
||||
after xdg-runtime
|
||||
}
|
||||
|
||||
start_pre() {
|
||||
checkpath -d -m 0755 -o "${NESTRI_USER}:${NESTRI_USER}" /run/nestri
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="Nestri media hub"
|
||||
|
||||
nestri_export_env
|
||||
|
||||
: "${NESTRI_UID:=1000}"
|
||||
: "${NESTRI_USER:=nestri}"
|
||||
|
||||
command="/usr/bin/neshub"
|
||||
# Unprivileged. The old nestri-guest-hub ran as root and mounted the
|
||||
# session's filesystem itself; the open neshub does neither — per its own
|
||||
# README it only muxes the Unix sockets the other components dial into one
|
||||
# QUIC endpoint. Whatever ends up owning session mounts (nesinit, presumably)
|
||||
# is a separate, still-closed piece.
|
||||
command_user="${NESTRI_USER}:${NESTRI_USER}"
|
||||
command_background="yes"
|
||||
pidfile="/run/nestri/neshub.pid"
|
||||
|
||||
# Where neshub's output goes. /nestri/logs is a virtiofs share mounted from
|
||||
# /etc/fstab at boot, before this service starts, so a hub that fails
|
||||
# immediately still leaves a record, and the record survives the VM.
|
||||
output_log="/nestri/logs/neshub.log"
|
||||
error_log="/nestri/logs/neshub.log"
|
||||
respawn="yes"
|
||||
respawn_delay="2"
|
||||
respawn_max="2" # NO infinite — if it fails, it fails for a reason
|
||||
|
||||
export XDG_RUNTIME_DIR="/run/user/${NESTRI_UID}"
|
||||
|
||||
depend() {
|
||||
need xdg-runtime
|
||||
use net
|
||||
after xdg-runtime
|
||||
}
|
||||
|
||||
start_pre() {
|
||||
checkpath -d -m 0755 -o "${NESTRI_USER}:${NESTRI_USER}" /run/nestri
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="Nestri pipewire audio sink"
|
||||
|
||||
nestri_export_env
|
||||
|
||||
: "${NESTRI_UID:=1000}"
|
||||
: "${NESTRI_USER:=nestri}"
|
||||
|
||||
command="/usr/bin/neswire"
|
||||
command_user="${NESTRI_USER}:${NESTRI_USER}"
|
||||
command_background="yes"
|
||||
pidfile="/run/nestri/neswire.pid"
|
||||
output_log="/nestri/logs/neswire.log"
|
||||
error_log="/nestri/logs/neswire.log"
|
||||
respawn="yes"
|
||||
respawn_delay="2"
|
||||
respawn_max="2"
|
||||
|
||||
export XDG_RUNTIME_DIR="/run/user/${NESTRI_UID}"
|
||||
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${NESTRI_UID}/bus"
|
||||
|
||||
depend() {
|
||||
# wireplumber is a hard dependency, not a nicety: start_post below pins
|
||||
# the default sink through the `default` metadata object, and
|
||||
# WirePlumber is what owns that object. Started concurrently, the pin
|
||||
# lands in an object pw-metadata created itself, which is destroyed the
|
||||
# moment it exits.
|
||||
need xdg-runtime dbus dbus-session pipewire wireplumber
|
||||
use neshub
|
||||
after pipewire wireplumber neshub
|
||||
}
|
||||
|
||||
start_pre() {
|
||||
checkpath -d -m 0755 -o "${NESTRI_USER}:${NESTRI_USER}" /run/nestri
|
||||
}
|
||||
|
||||
# Point the graph at neswire's sink, by name.
|
||||
#
|
||||
# `neswire` registers itself as media.class = Audio/Sink, node.name = neswire
|
||||
# ("Neswire Cloud Gaming Audio Sink"). With the hardware monitors off it is
|
||||
# the only sink, so WirePlumber's find-best hook should land on it anyway --
|
||||
# this makes it explicit rather than a consequence of there being nothing
|
||||
# else, so adding a second sink later cannot silently steal the default.
|
||||
#
|
||||
# `default.configured.audio.sink` is the metadata WirePlumber's find-selected
|
||||
# hook reads, and it takes a name; `wpctl set-default` takes a numeric object
|
||||
# id that changes every boot, which is why this uses pw-metadata instead.
|
||||
#
|
||||
# Not fatal if it fails: find-best still has one candidate. A game playing
|
||||
# into the wrong sink is silent, and a warning here is the only place that
|
||||
# would say so.
|
||||
start_post() {
|
||||
local i=0
|
||||
while [ $i -lt 50 ]; do
|
||||
pw-dump 2>/dev/null | grep -q '"node.name": "neswire"' && break
|
||||
sleep 0.1
|
||||
i=$((i+1))
|
||||
done
|
||||
if ! pw-dump 2>/dev/null | grep -q '"node.name": "neswire"'; then
|
||||
ewarn "neswire started but its sink never appeared in the graph"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Wait for WirePlumber to own the `default` metadata before writing to it.
|
||||
#
|
||||
# `need wireplumber` only guarantees its script returned, not that it has
|
||||
# built its objects. Writing too early is silently useless rather than an
|
||||
# error: pw-metadata creates the object, sets the key, exits 0, and the
|
||||
# object dies with the client. So wait for the object to exist, and say
|
||||
# so if it never does.
|
||||
i=0
|
||||
while [ $i -lt 50 ]; do
|
||||
pw-metadata -n default >/dev/null 2>&1 && break
|
||||
sleep 0.1
|
||||
i=$((i+1))
|
||||
done
|
||||
if ! pw-metadata -n default >/dev/null 2>&1; then
|
||||
ewarn "no 'default' metadata: WirePlumber is not running, sink not pinned"
|
||||
return 0
|
||||
fi
|
||||
|
||||
pw-metadata -n default 0 default.configured.audio.sink '{ "name": "neswire" }' \
|
||||
>/dev/null 2>&1
|
||||
|
||||
# Read it back. A write that did not stick is the failure this whole
|
||||
# sequence exists to catch, and it is invisible unless checked.
|
||||
if pw-metadata -n default 2>/dev/null | grep -q "neswire"; then
|
||||
einfo "default sink pinned to neswire"
|
||||
else
|
||||
ewarn "pinned neswire as default sink but it did not stick"
|
||||
fi
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="PipeWire multimedia daemon (system mode for nestri)"
|
||||
|
||||
nestri_export_env
|
||||
|
||||
: "${NESTRI_UID:=1000}"
|
||||
: "${NESTRI_USER:=nestri}"
|
||||
|
||||
command="/usr/bin/pipewire"
|
||||
command_user="${NESTRI_USER}:${NESTRI_USER}"
|
||||
command_background="yes"
|
||||
pidfile="/run/pipewire/pipewire.pid"
|
||||
|
||||
export XDG_RUNTIME_DIR="/run/user/${NESTRI_UID}"
|
||||
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/${NESTRI_UID}/bus"
|
||||
|
||||
depend() {
|
||||
need xdg-runtime dbus
|
||||
use dbus
|
||||
}
|
||||
|
||||
start_pre() {
|
||||
checkpath -d -m 0755 -o "${NESTRI_USER}:${NESTRI_USER}" /run/pipewire
|
||||
}
|
||||
|
||||
# `need pipewire` only waits for this script to return, and with
|
||||
# command_background that is the moment start-stop-daemon forks -- not the
|
||||
# moment PipeWire is accepting connections. With rc_parallel="YES" every
|
||||
# client (wireplumber, neswire) then races the socket and a client that
|
||||
# loses simply exits.
|
||||
#
|
||||
# dbus-session already solved exactly this for the bus socket. Same shape
|
||||
# here.
|
||||
start_post() {
|
||||
local i=0
|
||||
while [ ! -S "/run/user/${NESTRI_UID}/pipewire-0" ] && [ $i -lt 50 ]; do
|
||||
sleep 0.1
|
||||
i=$((i+1))
|
||||
done
|
||||
[ -S "/run/user/${NESTRI_UID}/pipewire-0" ] || {
|
||||
eerror "PipeWire socket never appeared"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="WirePlumber session manager for PipeWire"
|
||||
|
||||
nestri_export_env
|
||||
|
||||
: "${NESTRI_UID:=1000}"
|
||||
: "${NESTRI_USER:=nestri}"
|
||||
|
||||
command="/usr/bin/wireplumber"
|
||||
command_user="${NESTRI_USER}:${NESTRI_USER}"
|
||||
command_background="yes"
|
||||
pidfile="/run/pipewire/wireplumber.pid"
|
||||
|
||||
export XDG_RUNTIME_DIR="/run/user/${NESTRI_UID}"
|
||||
|
||||
depend() {
|
||||
need pipewire
|
||||
after pipewire
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/sbin/openrc-run
|
||||
|
||||
description="Create runtime directories (XDG_RUNTIME_DIR, X11 socket dir..)"
|
||||
|
||||
NESTRI_UID="${NESTRI_UID:-1000}"
|
||||
NESTRI_USER="${NESTRI_USER:-nestri}"
|
||||
|
||||
depend() {
|
||||
need localmount
|
||||
before dbus pipewire
|
||||
}
|
||||
|
||||
start() {
|
||||
ebegin "Preparing runtime directories"
|
||||
|
||||
if ! getent passwd "${NESTRI_USER}" >/dev/null 2>&1; then
|
||||
eerror "User ${NESTRI_USER} does not exist"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Per-user XDG runtime dir
|
||||
mkdir -p "/run/user/${NESTRI_UID}"
|
||||
chown "${NESTRI_USER}:${NESTRI_USER}" "/run/user/${NESTRI_UID}"
|
||||
chmod 0700 "/run/user/${NESTRI_UID}"
|
||||
|
||||
# X11 socket dir (Xwayland + any X clients expect this)
|
||||
mkdir -p /tmp/.X11-unix
|
||||
chown root:root /tmp/.X11-unix
|
||||
chmod 1777 /tmp/.X11-unix
|
||||
|
||||
# ICE socket dir - some toolkits look for it
|
||||
mkdir -p /tmp/.ICE-unix
|
||||
chown root:root /tmp/.ICE-unix
|
||||
chmod 1777 /tmp/.ICE-unix
|
||||
|
||||
eend $?
|
||||
}
|
||||
|
||||
stop() {
|
||||
ebegin "Removing runtime directories"
|
||||
rm -rf "/run/user/${NESTRI_UID}"
|
||||
# Don't rm /tmp/.X11-unix on stop - other things may be using it
|
||||
eend 0
|
||||
}
|
||||
@@ -14,14 +14,22 @@ if [ "$(id -u)" = "1000" ]; then
|
||||
export XDG_SESSION_TYPE="${XDG_SESSION_TYPE:-wayland}"
|
||||
export XDG_SESSION_DESKTOP="${XDG_SESSION_DESKTOP:-nestri}"
|
||||
|
||||
# Ensure proper VAAPI driver is used
|
||||
#export LIBVA_DRIVER_NAME="radeonsi"
|
||||
|
||||
# Force zink usage for OpenGL -> Vulkan translation
|
||||
#export __GLX_VENDOR_LIBRARY_NAME=mesa
|
||||
#export MESA_LOADER_DRIVER_OVERRIDE=zink
|
||||
#export GALLIUM_DRIVER=zink
|
||||
|
||||
# Ensure standard XDG dirs exist
|
||||
mkdir -p "${XDG_CONFIG_HOME}" "${XDG_DATA_HOME}" "${XDG_CACHE_HOME}" "${XDG_STATE_HOME}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# OpenGL -> Vulkan, for anybody who reaches a shell in here.
|
||||
#
|
||||
# Outside the uid test above on purpose, and duplicated from the init on
|
||||
# purpose. This file is only ever read by a person who got a shell in a box --
|
||||
# nothing a box runs is started from a login, so every service and every
|
||||
# workload is exec'd with a cleared environment and never sees this. The init
|
||||
# sets the same three for what it starts.
|
||||
#
|
||||
# It is here so that a debug shell renders the way a session does. A shell that
|
||||
# silently has no GL driver is how somebody concludes the image is broken while
|
||||
# the image is fine.
|
||||
export __GLX_VENDOR_LIBRARY_NAME=mesa
|
||||
export MESA_LOADER_DRIVER_OVERRIDE=zink
|
||||
export GALLIUM_DRIVER=zink
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# Nestri rootfs OpenRC config
|
||||
|
||||
rc_parallel="YES"
|
||||
rc_depend_strict="NO"
|
||||
rc_interactive="NO"
|
||||
rc_shell_timeout="0"
|
||||
# Every service's start/fail line, on the host directory that outlives the VM.
|
||||
#
|
||||
# Without this a guest nobody can log into reports nothing about its own boot:
|
||||
# a service that was skipped because a dependency failed looks exactly like a
|
||||
# service that was never registered. /nestri/logs is a virtiofs share, so the
|
||||
# record survives the guest — see the fstab entry below.
|
||||
rc_logger="YES"
|
||||
rc_log_path="/nestri/logs/rc.log"
|
||||
rc_verbose="NO"
|
||||
|
||||
# cgroup v2
|
||||
rc_cgroup_mode="unified"
|
||||
|
||||
# Don't bother trying to set hostname twice
|
||||
rc_hotplug="!net.*"
|
||||
|
||||
# Faster sulogin behavior on emergency
|
||||
rc_shell="/bin/sh"
|
||||
|
||||
# Default umask
|
||||
umask 022
|
||||
Reference in New Issue
Block a user