Files
takana/scripts/kde/plasma-start-qemu.sh
T
SergioandClaude Opus 5 807683dbc6 kde: XDG_MENU_PREFIX en los CINCO lanzadores — el menú de aplicaciones salía vacío
Arrancando la imagen KDE recién regenerada, el serial dijo:

    "applications.menu"  not found in  QList("/etc/xdg/menus")

y el escritorio pintó perfecto igual: fondo, panel, reloj, bandeja. Ése es justo el modo de fallo
que este repo persigue — nada falla, simplemente el menú de aplicaciones no tiene qué mostrar.

⚠ Y EL DIAGNÓSTICO OBVIO ERA EL EQUIVOCADO. Lo primero que anoté fue «falta el fichero, hay que
empaquetarlo». **No falta**: `plasma-workspace` instala `/etc/xdg/menus/plasma-applications.menu`
—verificado en el store Y en el rootfs de la imagen, 9904 bytes—. Lo que faltaba es el PREFIJO:
Plasma construye el nombre como `${XDG_MENU_PREFIX}applications.menu`, y sin la variable busca
`applications.menu` a secas, que no existe con ese nombre en NINGUNA distro con Plasma. El error
nombra un fichero que nunca tuvo ese nombre. La etiqueta contra el hecho, otra vez.

El arreglo es una línea. Lo que no era una línea es DÓNDE: los cinco lanzadores de Plasma exportan
`XDG_DATA_DIRS` y ninguno exportaba éste —comprobado uno por uno—, así que arreglar sólo el de QEMU
habría dejado la misma trampa en metal, metal-sw, el anidado y el headless. Es el cable que este
repo ya vio caer cinco veces en `cosecha-cron.sh` y en la frontera del sembrador: arreglar el caso
y dejar la trampa. Van los cinco.

  plasma-start-qemu.sh · plasma-start-metal.sh · plasma-start-metal-sw.sh   export
  nested-plasma.sh · run-plasma-headless.sh                                 --setenv (arman con bwrap)

VERIFICADO, y digo exactamente qué: que el nombre que Plasma construirá con el prefijo EXISTE en la
ruta donde lo busca (`/etc/xdg/menus/plasma-applications.menu` está en el rootfs de la imagen). Lo
que NO está verificado es el menú renderizado — eso pide reconstruir la imagen y arrancarla otra
vez. El `plasma-start` horneado en el rootfs fundido ya se refrescó con el script corregido, así que
la imagen queda a un rebuild de distancia.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCt3ettR4Z7b6wPCBmbvEV
2026-09-09 21:45:46 +00:00

228 lines
12 KiB
Bash

#!/bin/sh
# plasma-start-qemu.sh — arranca Plasma en una VM QEMU con render por SOFTWARE (kms_swrast/llvmpipe)
# sobre el DRM de virtio-gpu (/dev/dri/card0). Derivado de plasma-start-metal.sh pero SIN hardware iris:
# en QEMU no hay GPU real, así que kwin presenta por el DRM de virtio con EGL/GBM de software.
#
# Se instala como /usr/bin/plasma-start en la imagen QEMU y lo AUTO-LANZA /etc/profile (console-getty
# autologin). Trae los arreglos de la campaña KDE 2026-07: QT_QUICK_BACKEND=software (plasmashell no crea
# contexto GL sin él), QT_QPA_FONTDIR (mata los tofu; qtbase se construyó sin fontconfig), kdeglobals +
# QT_QPA_PLATFORMTHEME=kde (iconos + estilo Breeze), kactivitymanagerd (plasmashell aborta sin él).
#
# Logs: /tmp/{kwin,kamd,plasmashell}.log + tee a /dev/console. Salir: matar kwin.
set -u
# getty ejecuta este script DIRECTO (no vía login shell) ⇒ PATH viene vacío: mkdir/tr/cat/dbus-daemon
# no se hallan y kwin nunca levanta. Seteamos PATH explícito (busybox + userland KDE) ANTES de todo.
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
# tee a la consola gráfica (tty0) Y al serial (ttyS0) — el host captura ttyS0 con -serial file:
say() { echo "== plasma-qemu :: $*" | tee -a /dev/console /dev/ttyS0 2>/dev/null; }
dump() { echo "---- $1 ----" > /dev/ttyS0 2>/dev/null; cat "$1" > /dev/ttyS0 2>/dev/null; }
export LD_LIBRARY_PATH=/usr/lib64:/usr/lib/qt6:/usr/lib:/lib
export QT_PLUGIN_PATH=/usr/lib64/qt6/plugins:/usr/lib/qt6/plugins
export QML2_IMPORT_PATH=/usr/lib64/qt6/qml:/usr/lib/qt6/qml
export QML_IMPORT_PATH=$QML2_IMPORT_PATH
export XDG_DATA_DIRS=/usr/share
# ⚠ XDG_MENU_PREFIX: sin esto el MENÚ DE APLICACIONES sale VACÍO, y el fallo no se ve al arrancar.
# Medido el 2026-09-09 arrancando la imagen KDE: el serial dice
# "applications.menu" not found in QList("/etc/xdg/menus")
# y el escritorio pinta perfecto igual. **No falta el fichero**: `plasma-workspace` instala
# `/etc/xdg/menus/plasma-applications.menu`. Lo que falta es el PREFIJO que le dice a Plasma que
# busque ese nombre y no `applications.menu` a secas. Es la etiqueta contra el hecho: el error
# nombra un fichero que nunca existió con ese nombre en ninguna distro con Plasma.
export XDG_MENU_PREFIX=plasma-
export XDG_RUNTIME_DIR=/run/user/0
export XDG_CONFIG_HOME=/root/.config
# konsole toma el intérprete de $SHELL, NO de /etc/passwd, y este getty no es un login shell ⇒ sin
# esto la terminal abre con «Warning: Could not find '', starting '/bin/sh' instead. Please check
# your profile settings.» en rojo. El shell caía bien de casualidad; el aviso era real.
export SHELL=/bin/sh
export XDG_CACHE_HOME=/root/.cache
export HOME=/root
# --- render por software sobre virtio-gpu ---
export LIBGL_ALWAYS_SOFTWARE=1
export GALLIUM_DRIVER=llvmpipe
# GBM_ALWAYS_SOFTWARE=1 — EL FIX del segfault (MURO 2, verificado en fuente mesa 24.0.9). virtio-gpu-pci
# SIN 3D expone un nodo KMS-only (no render-capable). Con MESA_LOADER_DRIVER_OVERRIDE solo, gbm carga
# kms_swrast pero deja gbm_dri->software=FALSE (dri_screen_create tiene éxito ⇒ nunca cae al fallback
# dri_screen_create_sw, el único que setea software=true). Entonces dri2_initialize_drm entra al bloque
# `if(!software)` y llama gbm_dri->mesa->queryCompatibleRenderOnlyDeviceFd() — NULL en el driver software
# ⇒ SIGSEGV. GBM_ALWAYS_SOFTWARE=1 fuerza dri_screen_create_sw ⇒ software=true ⇒ se salta ese bloque.
export GBM_ALWAYS_SOFTWARE=1
# kms_swrast (NO swrast): kwin abre card0 como dispositivo KMS y crea un gbm device sobre él ⇒ necesita
# el driver de software para KMS (dumb buffers). Con 'swrast' a secas gbm_create_device falla ("Failed
# to create gbm device"). kms_swrast_dri.so está en /usr/lib/dri.
export MESA_LOADER_DRIVER_OVERRIDE=kms_swrast
export LIBGL_DRIVERS_PATH=/usr/lib/dri
export QT_QUICK_BACKEND=software
# LP_NUM_THREADS=1: llvmpipe SINCRÓNICO (sin pool de threads del rasterizador) — el JIT multi-thread de
# llvmpipe segfaultea al componer la 1ra superficie en esta VM. KWIN_DRM_NO_AMS=1: desactiva atomic
# modesetting (algunos virtio-gpu lo rompen). Ambos son fixes conocidos de kwin/llvmpipe en VMs.
export LP_NUM_THREADS=1
# KWIN_DRM_NO_AMS (legacy modeset) fue una corazonada del segfault (que resultó ser GBM_ALWAYS_SOFTWARE).
# Dejamos atomic (virtio soporta atomic nativo; legacy es el frágil).
# export KWIN_DRM_NO_AMS=1
#
# EL FIX DEL NEGRO: virtio-gpu ADVIERTE soportar drmModeAddFB2WithModifiers pero RECHAZA el modifier
# explícito (aún LINEAR=0) con EINVAL ⇒ "Failed to create a framebuffer" ⇒ sin output layer ⇒ NEGRO.
# El modeset inicial usa un buffer dumb (sin modifiers) y SÍ pinta (por eso se ve el wallpaper un instante),
# pero los buffers del compositor van por AddFB2WithModifiers y fallan ⇒ vuelve a negro. KWIN_DRM_USE_MODIFIERS=0
# fuerza a kwin a usar drmModeAddFB2 plano (modifier implícito) que virtio-gpu sí acepta.
export KWIN_DRM_USE_MODIFIERS=0
# KWIN_COMPOSE=Q — compositor QPainter (raster de Qt directo al buffer, SIN escena llvmpipe). El
# compositor GL por defecto usa el rasterizador de llvmpipe, cuyo hilo SEGFAULTEA en lp_rast_clear_color
# → util_fill_rect al limpiar el color buffer (backtrace del core.llvmpipe-0): kwin pinta unos frames y
# muere → "vuelve a negro". QPainter no arma escenas llvmpipe ⇒ esquiva ese crash. (El segfault viejo con
# =Q era el de gbm-init, ya resuelto por GBM_ALWAYS_SOFTWARE.) llvmpipe sigue disponible para gbm/EGL.
export KWIN_COMPOSE=Q
export QT_QPA_PLATFORM=wayland
export QT_QPA_PLATFORMTHEME=kde
export QT_QPA_FONTDIR=/usr/share/fonts/dejavu
# cursor: kwin busca el tema "default" y no lo halla ("Failed to load cursor theme") ⇒ cursor invisible.
# Apuntamos al breeze_cursors del rootfs (115 cursores) por env + kcminputrc (kwin lee [Mouse] cursorTheme).
export XCURSOR_THEME=breeze_cursors
export XCURSOR_PATH=/usr/share/icons:/usr/share/pixmaps
export XCURSOR_SIZE=24
# El tema ya carga (sin "Failed to load cursor theme") pero el cursor SIGUE invisible: kwin usa el plano
# de cursor por HARDWARE del DRM, y en virtio-gpu con render software ese plano no se presenta (aparece y
# desaparece). KWIN_FORCE_SW_CURSOR=1 ⇒ kwin dibuja el cursor DENTRO del framebuffer principal (software)
# ⇒ visible y estable en VMs sin GPU real.
export KWIN_FORCE_SW_CURSOR=1
# --- DIAGNÓSTICO del segfault-al-1er-composite (MURO 2) ---
# Sin gdb en la imagen: subimos el logging categorizado de kwin (kwin_core/drm/scene/opengl) + mesa/egl
# para ver EN QUÉ FASE muere (bind EGL, alloc GBM, JIT llvmpipe, o el paint real). DIAG=0 para apagarlo.
if [ "${DIAG:-1}" = 1 ]; then
export QT_LOGGING_RULES="kwin_*=true;qt.qpa.*=true"
export EGL_LOG_LEVEL=debug
export LIBGL_DEBUG=verbose
export MESA_DEBUG=1
export GALLIUM_HUD= # no HUD, sólo para dejar constancia
export KWIN_DRM_DEBUG=1
fi
mkdir -p "$XDG_RUNTIME_DIR" "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" /run/dbus /root
chmod 700 "$XDG_RUNTIME_DIR"
[ -s /etc/machine-id ] || cat /proc/sys/kernel/random/uuid 2>/dev/null | tr -d - > /etc/machine-id
# kdeglobals: iconos + estilo Breeze (sin esto Qt cae a hicolor vacío = escritorio pelado)
cat > "$XDG_CONFIG_HOME/kdeglobals" <<EOF
[Icons]
Theme=breeze
[General]
ColorScheme=BreezeLight
widgetStyle=Breeze
[KDE]
widgetStyle=Breeze
EOF
# kcminputrc: el tema de cursor que kwin lee al arrancar (además del env XCURSOR_THEME)
cat > "$XDG_CONFIG_HOME/kcminputrc" <<EOF
[Mouse]
cursorTheme=breeze_cursors
cursorSize=24
EOF
say "GPU DRM: $(ls /dev/dri/ 2>/dev/null | tr '\n' ' ')"
[ -e /dev/dri/card0 ] || say "!! no hay /dev/dri/card0 — ¿virtio-gpu no cargó? (dmesg | grep -i drm)"
# D-Bus de SISTEMA: sin él, kf.solid/udisks2 spamea "Not connected to D-Bus server" y powerdevil + los
# applets de dispositivos/energía no inician. Escucha en el path estándar /run/dbus/system_bus_socket
# (los clientes lo hallan por DBUS_SYSTEM_BUS_ADDRESS por defecto ⇒ no exportamos nada). Corre como root
# y hace setuid a messagebus (uid 81, sembrado en el passwd de la imagen). No hay udisksd/upowerd reales
# en el rootfs ⇒ no habrá discos/batería, pero el bus existe y solid deja de errorear en cada poll.
mkdir -p /run/dbus /var/lib/dbus
cp -f /etc/machine-id /var/lib/dbus/machine-id 2>/dev/null || true
if [ ! -S /run/dbus/system_bus_socket ]; then
if dbus-daemon --system --fork 2>/tmp/dbus-system.log; then
say "system bus ON (/run/dbus/system_bus_socket)"
else
say "!! system bus NO arrancó:"; dump /tmp/dbus-system.log
fi
fi
# D-Bus de sesión
if [ -z "${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
DBUS_SESSION_BUS_ADDRESS=$(dbus-daemon --session --fork --print-address 2>/dev/null)
export DBUS_SESSION_BUS_ADDRESS
fi
say "dbus: ${DBUS_SESSION_BUS_ADDRESS:-NO ARRANCO}"
# --- CORE DUMP del segfault (MURO 2): sin gdb en la imagen, sacamos un core y lo leemos con el gdb del
# host apuntando al rootfs fundido. Localiza el frame culpable (libLLVM/llvmpipe vs kwin vs mesa). ---
if [ "${DIAG:-1}" = 1 ]; then
ulimit -c unlimited 2>/dev/null || true
# /core en la RAÍZ ext4 (NO /tmp: es tmpfs=RAM y se pierde al matar QEMU). Tras el crash hacemos sync
# explícito ⇒ el core persiste en la imagen aunque después mate QEMU con SIGKILL.
echo '/core.%e.%p' > /proc/sys/kernel/core_pattern 2>/dev/null || true
say "core dumps ON (pattern=$(cat /proc/sys/kernel/core_pattern 2>/dev/null), ulimit -c=$(ulimit -c))"
fi
# SÓLO el card de virtio-gpu: el efifb/simpledrm sobrevive como 2º DRM ("Unknown-1", read-only) y kwin
# intenta manejar ambos outputs ⇒ conflicto de presentación ⇒ pinta y vuelve a NEGRO. Detectamos el card
# cuyo driver es virtio_gpu (robusto al numerado card0/card1 que cambia entre boots) y se lo pasamos a kwin.
VIRTIO_CARD=
for c in /dev/dri/card*; do
n=$(basename "$c")
drv=$(readlink -f "/sys/class/drm/$n/device/driver" 2>/dev/null)
case "$drv" in *virtio*) VIRTIO_CARD="$c"; break;; esac
done
if [ -n "$VIRTIO_CARD" ]; then
export KWIN_DRM_DEVICES="$VIRTIO_CARD"
say "KWIN_DRM_DEVICES=$VIRTIO_CARD (ignorando efifb/simpledrm)"
else
say "!! no hallé card virtio_gpu; kwin verá todos los DRM (posible doble output)"
fi
# Compositor sobre el DRM de virtio (software EGL/GBM vía kms_swrast). SIN --virtual: presenta al display.
say "lanzando kwin_wayland (DRM virtio, software GL)..."
/usr/bin/kwin_wayland --drm >/tmp/kwin.log 2>&1 &
KWIN=$!
# stream EN VIVO del kwin.log al serial (busybox sed NO soporta -u ⇒ nada de sed/pipes: tail -f directo).
( echo "==== kwin.log (stream vivo) ===="; tail -f /tmp/kwin.log ) > /dev/ttyS0 2>/dev/null &
TAILPID=$!
i=0
while [ $i -lt 60 ]; do
[ -S "$XDG_RUNTIME_DIR/wayland-0" ] && break
kill -0 $KWIN 2>/dev/null || { say "!! kwin murio:"; dump /tmp/kwin.log; exec /bin/sh; }
i=$((i+1)); sleep 0.5
done
if [ -S "$XDG_RUNTIME_DIR/wayland-0" ]; then
say "compositor OK (wayland-0)"
else
say "!! kwin no expuso wayland-0:"; dump /tmp/kwin.log; exec /bin/sh
fi
export WAYLAND_DISPLAY=wayland-0
# kactivitymanagerd: plasmashell ABORTA sin el
KAMD=$(ls /usr/lib*/libexec/kactivitymanagerd 2>/dev/null | head -1)
[ -n "$KAMD" ] && { "$KAMD" >/tmp/kamd.log 2>&1 & sleep 2; say "kactivitymanagerd lanzado"; }
say "lanzando plasmashell..."
/usr/bin/plasmashell >/tmp/plasmashell.log 2>&1 &
sleep 8
say "procesos: $(ps axo comm 2>/dev/null | grep -E 'kwin|plasma' | sort -u | tr '\n' ' ')"
say "escritorio arriba (o mira /tmp/plasmashell.log). Ctrl-C mata la sesion."
dump /tmp/plasmashell.log
# monitor de estado: cada 15s dice si kwin/plasmashell siguen vivos ⇒ si vuelve a negro, sabremos si
# fue plasmashell que murió (containment) o kwin/output. (busybox pgrep -c cuenta procesos.)
( n=0; while sleep 15; do
n=$((n+1))
say "STATUS +$((n*15))s: kwin=$(pgrep kwin_wayland 2>/dev/null | wc -l) plasmashell=$(pgrep plasmashell 2>/dev/null | wc -l)"
done ) &
wait $KWIN
RC=$?
# kwin salió ⇒ volcá su log para saber por qué (backend QPainter, DRM, modeset…)
say "kwin salió (código $RC) — volcando kwin.log:"
dump /tmp/kwin.log
# copiar los logs a la ext4 raíz y SYNC ⇒ core + logs persisten en la imagen para leer desde el host
# con debugfs/gdb (el SIGKILL de QEMU no sincroniza; sin este sync se pierde todo lo de esta sesión).
cp /tmp/kwin.log /kwin.log 2>/dev/null || true
cp /tmp/plasmashell.log /plasmashell.log 2>/dev/null || true
say "cores en /: $(ls -la /core.* 2>/dev/null | tr '\n' ' ' || echo NINGUNO)"
sync; sync
say "== disco SYNCED (core+logs en la raíz). serial CONGELADO. matá QEMU desde el host. =="
sleep 3600