#!/bin/sh # cosmic-start — levanta la sesión COSMIC. Lo corre el getty (o el usuario a mano), y es EL MISMO # fichero en la imagen de QEMU y en la de metal. Hermano de gnome-start y plasma-start; hereda de # ellos todo lo que ya está pago y sólo diverge en lo que COSMIC necesita. # # ⚠ SE LLAMABA `cosmic-start-qemu.sh` Y ESO COSTÓ UN VIAJE FÍSICO (2026-08-05). El nombre decía la # verdad —forzaba render por software con `LIBGL_ALWAYS_SOFTWARE` y `MESA_LOADER_DRIVER_OVERRIDE= # kms_swrast`, que es lo correcto sobre virtio-gpu— pero la imagen de METAL lo instalaba igual, con # un comentario que afirmaba que «nada de lo que hace es específico del emulador». En un Coffee Lake # real la mesa del corpus es `iris` y NO EXISTE ningún `kms_swrast_dri.so`: el override no degrada a # software, hace fallar EGL y el compositor no arranca. Y en el mejor caso habría sido peor: si # hubiera arrancado, habría compuesto por software y ScreenCast habría fallado por el mismo motivo # que en QEMU ⇒ **el viaje no podía contestar la pregunta que lo motivaba**. # Por eso ahora el modo de GL se DETECTA (ver más abajo) en vez de hardcodearse, y el fichero se # llama por lo que hace y no por dónde lo probamos. # # Es `/bin/sh` (busybox ash) a propósito, aunque `start-cosmic` de upstream sea bash: este script # corre ANTES, prepara el entorno, y después le pasa la posta. Cuanto menos dependa, más temprano # puede reportar. set -u # PATH ARRIBA DE TODO, y no es cosmético. El getty entrega un PATH que NO trae /usr/bin, y en esta # imagen `mkdir` sólo existe ahí (`/usr/bin/mkdir → coreutils`; la base metal no tendió el symlink # `/bin/mkdir → busybox` que sí tienen chmod, mount, sed y tee). Resultado: los cuatro `mkdir -p` de # la preparación fallaban con «mkdir: not found» y la sesión arrancaba IGUAL, porque los directorios # venían horneados en la imagen. Un fallo que no se cobra nada hasta que alguien cambia la imagen. export PATH=/usr/bin:/bin:/usr/sbin:/sbin # ── DÓNDE VAN LOS LOGS: al DISCO, no a /tmp ───────────────────────────────────────────────────── # /tmp es tmpfs, o sea RAM. En QEMU da igual porque el serial queda en el terminal del que lanzó la # VM; en METAL, si la sesión falla, la evidencia se evapora al apagar y el viaje físico se gasta sin # aprender nada — pasó exactamente así. /var/log/cosmic vive en la ext4 de la imagen ⇒ sobrevive al # apagón y se puede leer desde otra máquina sin bootear, con `debugfs -R "dump ..." /dev/sdX2`. LOG=/var/log/cosmic mkdir -p "$LOG" 2>/dev/null [ -d "$LOG" ] && [ -w "$LOG" ] || LOG=/tmp # ⚠⚠ NUNCA ESCRIBIR A /dev/ttyS0 A CIEGAS — CUELGA EL ARRANQUE EN METAL ⚠⚠ # Abrir un puerto serie sin portadora BLOQUEA en open() esperando DCD, salvo que el termios tenga # CLOCAL. El kernel pone CLOCAL cuando registra el puerto COMO CONSOLA, y eso ocurre en QEMU porque el # cmdline lleva `console=ttyS0,115200`. En el metal del OptiPlex 3060 la firmware NO aplica el # CONFIG_CMDLINE horneado (el kernel arranca con `Command line:` VACÍO) ⇒ ttyS0 es un puerto común, # sin cable, sin portadora: el PRIMER `say` se colgaba ahí para siempre. Síntoma exacto y desconcertante: # /var/log/cosmic se creaba (el mkdir va antes) pero el .log nunca aparecía, y redirigir a fichero # daba vacío — o sea que el script parecía no ejecutarse cuando en realidad estaba bloqueado en su # primera línea de salida. Diagnosticado por el usuario quitando los pipes, 2026-08-05. # # El serial no se pierde: cuando ES la consola, /dev/console YA ES ttyS0. Cuando no lo es, no había # nada que escuchara del otro lado. Escribir al fichero de log es lo que sirve en metal. # # Y /dev/console sólo si stdout no es ya un terminal: lanzado a mano desde el getty, stdout ES la # pantalla y /dev/console (=tty0 con cmdline vacío) es LA MISMA ⇒ cada línea salía DOS VECES. [ -t 1 ] && CONS="" || CONS=/dev/console say() { echo "== cosmic :: $*" | tee -a $CONS "$LOG/cosmic-start.log" 2>/dev/null; } # `dump` iba SÓLO al serial: en metal eso es a la vez escribir a la nada Y COLGARSE (ver arriba). Por # eso el 3er viaje terminaba siempre en «log del compositor (primer tramo)»: no era el último paso, # era donde se bloqueaba. Ahora va al fichero de log, que es lo que se puede leer después. dump() { { echo "---- $1 ----"; cat "$1" 2>/dev/null; } >> "$LOG/cosmic-start.log" 2>/dev/null; } alive() { s=$(sed -n 's/^State:[[:space:]]*//p' /proc/$1/status 2>/dev/null); case "$s" in ""|Z*) return 1;; *) return 0;; esac; } say "arrancando (pid $$)" # El getty ejecuta este script SIN ambiente heredable, así que las perillas de la imagen viajan como # FICHERO —mismo truco que `/etc/gnome-mode`—. Hoy sólo `COSMIC_MODE` (bare|session). [ -r /etc/cosmic-mode ] && . /etc/cosmic-mode # ── FS virtuales y directorios de runtime ─────────────────────────────────────────────────────── mount -t proc none /proc 2>/dev/null mount -t sysfs none /sys 2>/dev/null mount -t devtmpfs none /dev 2>/dev/null mkdir -p /dev/pts /dev/shm && mount -t devpts none /dev/pts 2>/dev/null && mount -t tmpfs none /dev/shm 2>/dev/null # XDG_RUNTIME_DIR es OBLIGATORIO para Wayland: ahí viven el socket del compositor y los de los # clientes. Sin él el compositor arranca y nadie puede conectarse — y el error que da es del cliente, # no del compositor, que es lo que hace confuso el diagnóstico. export XDG_RUNTIME_DIR=/run/user/0 mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR" # …y COMPROBARLO, porque el primer arranque falló justo acá y el error salía del sitio equivocado: # dbus decía «XDG_RUNTIME_DIR /run/user/0 not available» y eso se lee como un problema de dbus. El # `mkdir -p` de arriba no garantiza nada si /run no es escribible o si algo lo montó encima. if [ -d "$XDG_RUNTIME_DIR" ] && [ -w "$XDG_RUNTIME_DIR" ]; then say "XDG_RUNTIME_DIR OK ($XDG_RUNTIME_DIR)" else say "!! XDG_RUNTIME_DIR NO existe/no escribible — /run es: $(ls -ld /run 2>&1)" say " montando tmpfs en /run" mount -t tmpfs none /run 2>/dev/null mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR" [ -w "$XDG_RUNTIME_DIR" ] && say " ✓ ahora sí" || say " ✗ sigue sin andar" fi mkdir -p /root/.config /root/.cache /var/lib/dbus /run/dbus /tmp && chmod 1777 /tmp export HOME=/root SHELL=/bin/bash # PATH ya salió arriba, antes del primer mkdir say "kernel $(uname -r) · drm: $(ls /dev/dri 2>/dev/null | tr '\n' ' ')" # ── GL: HARDWARE si lo hay, software si no — DETECTADO, nunca hardcodeado ─────────────────────── # La regla tiene DOS patas y hay que cumplir las dos: que el KERNEL exponga un driver DRM real # (i915/amdgpu/radeon/nouveau, no virtio_gpu ni vgem), y que MESA traiga el `.so` correspondiente. # Con una sola no alcanza, y por eso el hardcodeo anterior era una trampa: la mesa del corpus va con # `-Dgallium-drivers=iris -Dllvm=disabled`, así que sobre un AMD real habría kernel pero no userland, # y en QEMU hay virtio-gpu pero el camino bueno es swrast. Si no se cumple NINGUNA, se dice fuerte en # vez de morir adentro de EGL con un mensaje que no señala la causa. DRM_DRV="" for _c in /sys/class/drm/card[0-9]*; do [ -r "$_c/device/uevent" ] || continue DRM_DRV=$(sed -n 's/^DRIVER=//p' "$_c/device/uevent" 2>/dev/null) [ -n "$DRM_DRV" ] && break done DRI=/usr/lib/dri case "$DRM_DRV" in i915) MESA_HW=iris ;; amdgpu) MESA_HW=radeonsi ;; radeon) MESA_HW=r600 ;; nouveau) MESA_HW=nouveau ;; *) MESA_HW="" ;; esac if [ -n "$MESA_HW" ] && [ -r "$DRI/${MESA_HW}_dri.so" ]; then say "GL por HARDWARE — kernel drm=$DRM_DRV + ${MESA_HW}_dri.so (sin overrides de software)" elif [ -r "$DRI/kms_swrast_dri.so" ] || [ -r "$DRI/swrast_dri.so" ]; then say "GL por SOFTWARE — drm=${DRM_DRV:-ninguno}, no hay userland HW ⇒ kms_swrast" # `GBM_ALWAYS_SOFTWARE` es lo que hace que mesa acepte crear un device GBM sobre virtio-gpu sin # acelerador; `LP_NUM_THREADS=1` evita que llvmpipe se coma los vcpus y deje al compositor sin # planificar. Las dos vienen pagas de las campañas KDE y GNOME. export GBM_ALWAYS_SOFTWARE=1 LIBGL_ALWAYS_SOFTWARE=1 MESA_LOADER_DRIVER_OVERRIDE=kms_swrast LP_NUM_THREADS=1 else say "!! NI hardware NI software: drm=${DRM_DRV:-ninguno}; en $DRI sólo hay: $(ls $DRI 2>/dev/null | tr '\n' ' ')" say " el compositor va a fallar al inicializar EGL — ESTO es la causa, no un síntoma posterior" fi # COSMIC pinta con tiny-skia (libcosmic entra sin `wgpu`), pero el compositor SÍ usa EGL/GL por # smithay para componer, así que el modo de GL lo decide todo. export RUST_BACKTRACE=1 export RUST_LOG="${RUST_LOG:-info}" # ── seatd: quién le da los devices al compositor ──────────────────────────────────────────────── # smithay entra con `backend_session_libseat`, y libseat tiene dos backends: hablar con un `seatd` # corriendo, o —si el proceso ya es root— abrir los devices él mismo (`builtin`). Acá se levanta # seatd de verdad en vez de usar el builtin, porque es el camino que también sirve fuera de QEMU y # porque deja un log donde mirar. Si no sube, se cae al builtin: mejor un escritorio con andamio que # ninguno, pero se dice en el serial para que no pase por bueno. if [ -x /usr/bin/seatd ]; then /usr/bin/seatd -g video >$LOG/seatd.log 2>&1 & SEATD_PID=$! sleep 1 if alive $SEATD_PID && [ -S /run/seatd.sock ]; then say "seatd OK (pid $SEATD_PID, /run/seatd.sock)" else say "!! seatd no levantó — caigo a LIBSEAT_BACKEND=builtin (somos root)"; dump $LOG/seatd.log export LIBSEAT_BACKEND=builtin fi else say "!! sin seatd — LIBSEAT_BACKEND=builtin" export LIBSEAT_BACKEND=builtin fi # ── bus de SISTEMA ────────────────────────────────────────────────────────────────────────────── # cosmic-comp habla logind por zbus (sesión, inhibidores) y el bus de sesión lo abre después # `start-cosmic` con dbus-run-session. El de sistema hay que levantarlo acá. if [ -x /usr/bin/dbus-daemon ]; then # ⚠ El mkdir de /run/dbus va ACÁ, no arriba con los demás. El primer arranque en modo bare falló # con «Failed to bind socket /run/dbus/system_bus_socket: No such file or directory» aunque el # directorio se creaba al principio del script: entre un punto y otro arje-zero sigue montando, y # un tmpfs sobre /run se lleva puesto lo que hubiera. Crear justo antes de usar es la única forma # de no depender de ese orden. Mismo motivo que el segundo mkdir de XDG_RUNTIME_DIR. mkdir -p /run/dbus /var/lib/dbus [ -f /var/lib/dbus/machine-id ] || dbus-uuidgen > /var/lib/dbus/machine-id 2>/dev/null [ -f /etc/machine-id ] || cp /var/lib/dbus/machine-id /etc/machine-id 2>/dev/null dbus-daemon --system --fork >$LOG/dbus-system.log 2>&1 sleep 1 [ -S /run/dbus/system_bus_socket ] && say "bus de sistema OK" || { say "!! sin bus de sistema:"; dump $LOG/dbus-system.log; } else say "!! sin dbus-daemon" fi # ── login1: el shim de arje ───────────────────────────────────────────────────────────────────── # Mismo eslabón que en GNOME y KDE. cosmic-comp pregunta por su sesión; quien contesta # `org.freedesktop.login1` acá es arje-logind-compat. Se ESPERA el nombre, no el pid: un pid no es un # servicio, y esperar sólo al final deja pasar la carrera (lección de gnome-start, 2026-08-03). if [ -x /usr/bin/arje-logind-compat ]; then ARJE_LOGIN_STATE=1 /usr/bin/arje-logind-compat >$LOG/logind-compat.log 2>&1 & say "arje-logind-compat lanzado (pid $!)" i=0 while [ $i -lt 20 ]; do if dbus-send --system --dest=org.freedesktop.DBus --print-reply /org/freedesktop/DBus \ org.freedesktop.DBus.NameHasOwner string:org.freedesktop.login1 2>/dev/null | grep -q "boolean true"; then say "bus: org.freedesktop.login1 OK"; break fi i=$((i+1)); sleep 0.5 done [ $i -lt 20 ] || { say "!! login1 NO apareció en 10s:"; dump $LOG/logind-compat.log; } else say "!! sin arje-logind-compat — cosmic-comp puede no hallar sesión" fi # ── COSMIC ────────────────────────────────────────────────────────────────────────────────────── export XDG_CURRENT_DESKTOP=COSMIC XDG_SESSION_DESKTOP=COSMIC XDG_SESSION_TYPE=wayland export XDG_SESSION_ID=1 XDG_SEAT=seat0 XDG_VTNR=1 export XDG_DATA_DIRS=/usr/share XDG_CONFIG_DIRS=/etc/xdg # Y otra vez JUSTO ANTES de arrancar, no sólo al principio: entre el getty y este punto pasan varios # segundos en los que arje-zero sigue montando cosas, y un tmpfs sobre /run se lleva puesto el # directorio sin avisar. Comprobar al principio y usar al final es cómo se cuela una carrera. mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR" [ -w "$XDG_RUNTIME_DIR" ] || say "!! XDG_RUNTIME_DIR se perdió entre el arranque y ahora" # ── EL DEMONIO DE PIPEWIRE ────────────────────────────────────────────────────────────────────── # Faltaba, y el hueco no se veía: `/usr/bin/pipewire` está en la imagen desde que entró como dep de # `cosmic-settings-daemon`, pero NADIE lo arrancaba. En una distro con systemd de usuario lo levanta # `pipewire.socket`; acá no hay tal cosa, así que es tarea de este script — el mismo patrón que el # bus de sistema y que login1. # # La consecuencia medida de que faltara: `org.freedesktop.portal.ScreenCast.CreateSession` ACEPTA y # devuelve su objeto Request —o sea que la cadena del portal está bien—, pero no puede haber stream, # porque el backend negocia el nodo contra un demonio que no existe. Un fallo que se manifiesta como # «no pasa nada» tres capas más arriba de la causa. # # Va ACÁ y no junto al bus de sesión a propósito: pipewire no necesita D-Bus para arrancar, sólo # XDG_RUNTIME_DIR (es donde abre su socket). Ponerlo justo después de asegurar ese directorio lo hace # válido para los DOS modos —bare y session— sin duplicar el bloque. # # ⚠ Sin `wireplumber` (el gestor de sesión, que ni siquiera está construido) pipewire arranca y sirve, # pero no gestiona dispositivos: no descubre tarjetas ni enruta audio. Para SCREENCAST eso alcanza — # el backend del portal crea su propio nodo de video— y para AUDIO no. Son dos preguntas distintas y # esta pieza sólo contesta la primera. if [ -x /usr/bin/pipewire ]; then /usr/bin/pipewire >$LOG/pipewire.log 2>&1 & PW_PID=$! # El socket, no el pid: misma disciplina que el compositor. `pipewire-0` es el nombre por defecto # (`core.name` en pipewire.conf); si el demonio muere al instante, el pid sigue "existiendo" un # rato y esperar por él daría un OK falso. i=0 while [ $i -lt 20 ]; do [ -S "$XDG_RUNTIME_DIR/pipewire-0" ] && { say "pipewire OK (socket pipewire-0, pid $PW_PID)"; break; } alive $PW_PID || { say "!! pipewire MURIÓ antes de abrir el socket:"; dump $LOG/pipewire.log; break; } i=$((i+1)); sleep 0.5 done [ -S "$XDG_RUNTIME_DIR/pipewire-0" ] || { say "!! pipewire sin socket en $((i/2))s:"; dump $LOG/pipewire.log; } else say "!! sin /usr/bin/pipewire — ScreenCast no va a poder crear stream" fi # ── EL GESTOR DE SESIÓN: wireplumber ──────────────────────────────────────────────────────────── # pipewire se construye con `-Dsession-managers=[]`, o sea que arranca y sirve pero NO TIENE # POLÍTICA: nada decide qué es entrada, qué es salida, ni quién mueve un nodo de `Paused` a # `Streaming`. wireplumber es esa política, escrita en Lua. # # Por qué está acá y no antes: **necesita el socket de pipewire ya abierto**. Se lanza después a # propósito, no por orden estético — un gestor de sesión sin demonio que gestionar sale con error. # # Acá NO se puede esperar un socket, porque wireplumber no abre ninguno: es un CLIENTE de pipewire, # no un servidor. Lo que dice que hizo su trabajo es que aparezca como cliente en el grafo # (`pw-cli ls Client`), y eso es una medición del sistema, no una condición de arranque. Así que se # espera un instante y se reporta si el proceso sigue vivo — con el log a mano, que es lo que sirve # cuando falla. if [ -x /usr/bin/wireplumber ] && [ -S "$XDG_RUNTIME_DIR/pipewire-0" ]; then /usr/bin/wireplumber >$LOG/wireplumber.log 2>&1 & WP_PID=$! sleep 2 if alive $WP_PID; then say "wireplumber OK (pid $WP_PID) — hay gestor de sesión" else say "!! wireplumber MURIÓ:"; dump $LOG/wireplumber.log fi elif [ -x /usr/bin/wireplumber ]; then say "!! wireplumber presente pero sin socket de pipewire — no se lanza" else say "-- sin wireplumber (pipewire sirve, pero sin política de sesión)" fi # ── FONDO: color liso, porque la imagen por defecto NO SE PUEDE EMPAQUETAR ──────────────────── # El default de cosmic-bg apunta a `/usr/share/backgrounds/cosmic/orion_nebula_nasa_heic0601a.jpg`, # que vive en el repo `cosmic-wallpapers`… en **git-lfs**: el tarball de GitHub pesa 20 KB y trae # punteros, no imágenes. O sea que la receta obvia produciría un paquete de ficheros de texto. # Mientras eso no se resuelva, se escribe la config de USUARIO con una fuente `Color`, que # cosmic-bg soporta de fábrica. Va acá y no en el artefacto a propósito: es política de esta # imagen de prueba, no algo que el paquete prometa. if [ ! -f /usr/share/backgrounds/cosmic/orion_nebula_nasa_heic0601a.jpg ]; then mkdir -p "$HOME/.config/cosmic/com.system76.CosmicBackground/v1" cat > "$HOME/.config/cosmic/com.system76.CosmicBackground/v1/all" <<'RON' ( output: "all", source: Color(Single((0.13, 0.29, 0.53))), filter_by_theme: true, rotation_frequency: 3600, filter_method: Lanczos, scaling_mode: Zoom, sampling_method: Alphanumeric, ) RON say "fondo: color liso (el wallpaper por defecto vive en git-lfs y no se empaqueta)" fi # ── SEMILLA DE CONFIG DE qalc, SIN LA CUAL LA CALCULADORA DEL LANZADOR NUNCA ANDA ──────────────── # En el primer arranque `qalc` hace una PREGUNTA interactiva («¿activar autocalc?») antes de leer # nada. El plugin `calc` de pop-launcher le escribe la expresión y cierra el pipe, así que la # expresión se consume como respuesta, no es un sí/no válido, y qalc **no llega a guardar config** ⇒ # la próxima vez vuelve a preguntar. O sea que sin sembrar esto no falla una vez: falla SIEMPRE. # Es política de la imagen, no del paquete —igual que el color de fondo—: una distro de verdad lo # pondría en el esqueleto de /etc/skel. if [ ! -f "$HOME/.config/qalculate/qalc.cfg" ]; then mkdir -p "$HOME/.config/qalculate" printf '[General]\ncalculate_as_you_type=0\n' > "$HOME/.config/qalculate/qalc.cfg" say "qalc: config sembrada (si no, la pregunta de primer arranque se come la cuenta)" fi # ── MODO: bare (default) o session ────────────────────────────────────────────────────────────── # `cosmic-session` llama a `cosmic-settings-daemon` con # `.expect("failed to start settings daemon")` (src/main.rs:255) y PANICKEA si no está — no es # opcional ni está detrás de una feature. # # ⚠ **DESACTUALIZADO Y CORREGIDO (2026-08-05)**: acá decía que ese daemon «todavía no se puede # construir acá (arrastra pipewire)». Es falso desde hace tiempo — `cosmic-settings-daemon` está # sellado y corre, y `pipewire` está en la imagen. Se deja el rastro porque el comentario viejo # mandaba a buscar un bloqueo que ya no existe. # # Lo que SÍ faltaba, y era otra cosa, **ya está**: nadie arrancaba el demonio de pipewire. Se # corrigió arriba (bloque «EL DEMONIO DE PIPEWIRE», justo después de XDG_RUNTIME_DIR). Queda # `wireplumber` sin construir, que es lo que separa «hay stream de video» de «hay audio». # # El modo `bare` es la otra mitad que upstream ya soporta (`make install-bare-session`): el # compositor se lanza SOLO y los clientes se levantan a mano contra su WAYLAND_DISPLAY. Sirve para lo # que hace falta ahora, que es saber si el escritorio PINTA — y separa las dos preguntas: «¿compone?» # es del compositor, «¿arranca la sesión?» es del gestor. # # COSMIC_MODE=session para probar el camino de producción cuando el daemon exista. if [ "${COSMIC_MODE:-bare}" = session ]; then say "lanzando cosmic-session vía start-cosmic (modo session)" /bin/bash /usr/bin/start-cosmic --in-login-shell >$LOG/cosmic-session.log 2>&1 & CS_PID=$! else say "lanzando cosmic-comp SOLO (modo bare; cosmic-session necesita settings-daemon)" dbus-daemon --session --fork --print-address=1 >$LOG/dbus-session-addr 2>$LOG/dbus-session.log DBUS_SESSION_BUS_ADDRESS=$(cat $LOG/dbus-session-addr 2>/dev/null) [ -n "$DBUS_SESSION_BUS_ADDRESS" ] && export DBUS_SESSION_BUS_ADDRESS && say "bus de sesión OK" \ || { say "!! sin bus de sesión:"; dump $LOG/dbus-session.log; } /usr/bin/cosmic-comp >$LOG/cosmic-session.log 2>&1 & CS_PID=$! fi # ── Reporte: el socket, no el pid ─────────────────────────────────────────────────────────────── # «Un pid no es un servicio» — la lección que costó dos iteraciones en GNOME. Lo que dice que el # compositor está VIVO es que exista su socket Wayland, no que el proceso no haya muerto todavía. i=0; WD="" while [ $i -lt 60 ]; do WD=$(ls "$XDG_RUNTIME_DIR" 2>/dev/null | grep -m1 '^wayland-[0-9]$') if [ -n "$WD" ]; then say "compositor OK ($WD) — cosmic-comp ES el display server" break fi alive $CS_PID || { say "!! la sesión MURIÓ antes de crear el socket:"; dump $LOG/cosmic-session.log; break; } i=$((i+1)); sleep 1 done [ -n "$WD" ] || say "!! sin socket wayland en ${i}s:" [ -n "$WD" ] || dump $LOG/cosmic-session.log # ── Los clientes, contra el socket que el compositor haya elegido ─────────────────────────────── # En modo bare no hay quien los levante: `cosmic-session` es el que normalmente hace esto, y por eso # cada uno recibe WAYLAND_DISPLAY explícito. Se lanza sólo lo que ESTÉ instalado — la cola COSMIC se # construye por partes, y un `[ -x ]` de más es más barato que un arranque perdido. if [ -n "$WD" ] && [ "${COSMIC_MODE:-bare}" != session ]; then export WAYLAND_DISPLAY="$WD" for c in cosmic-bg cosmic-panel cosmic-osd cosmic-notifications cosmic-app-library cosmic-launcher cosmic-idle; do if [ -x "/usr/bin/$c" ]; then "/usr/bin/$c" >"$LOG/$c.log" 2>&1 & say "cliente $c lanzado (pid $!)" sleep 1 fi done fi # Estado periódico + el log completo al final de cada tramo: el dato que importa no es que siga vivo # sino QUÉ componentes levantó la sesión. n=0 while [ $n -lt 12 ]; do sleep 15; n=$((n+1)) # `pgrep -c` de busybox devolvía 0 con el compositor VIVO y su socket abierto — o sea que el # contador mentía. Se cuentan los /proc/*/comm, que es el dato del kernel y no una utilería. say "STATUS +$((n*15))s: $(for c in /proc/[0-9]*/comm; do cat "$c" 2>/dev/null; done | grep -c '^cosmic') procesos cosmic · $(for c in /proc/[0-9]*/comm; do cat "$c" 2>/dev/null; done | grep '^cosmic' | sort -u | tr '\n' ' ')" [ $n = 2 ] && { say "log del compositor (primer tramo)"; dump $LOG/cosmic-session.log; for l in $LOG/cosmic-bg.log $LOG/cosmic-panel.log; do [ -s "$l" ] && dump "$l"; done; } done say "fin de la ventana de observación" dump $LOG/cosmic-session.log exec /bin/sh