sergio/takana
> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…
🎥 cosmic: ScreenCast — dos bloqueos, y el que importa es que NADIE ARRANCA PIPEWIRE
1#!/bin/sh
2# cosmic-start — lo que el console-getty ejecuta en vez de /bin/sh en la imagen COSMIC de QEMU.
3# Hermano de gnome-start y plasma-start; hereda de ellos todo lo que ya está pago y sólo diverge en
4# lo que COSMIC necesita.
5#
6# Es `/bin/sh` (busybox ash) a propósito, aunque `start-cosmic` de upstream sea bash: este script
7# corre ANTES, prepara el entorno, y después le pasa la posta. Cuanto menos dependa, más temprano
8# puede reportar.
9set -u
10# PATH ARRIBA DE TODO, y no es cosmético. El getty entrega un PATH que NO trae /usr/bin, y en esta
11# imagen `mkdir` sólo existe ahí (`/usr/bin/mkdir → coreutils`; la base metal no tendió el symlink
12# `/bin/mkdir → busybox` que sí tienen chmod, mount, sed y tee). Resultado: los cuatro `mkdir -p` de
13# la preparación fallaban con «mkdir: not found» y la sesión arrancaba IGUAL, porque los directorios
14# venían horneados en la imagen. Un fallo que no se cobra nada hasta que alguien cambia la imagen.
15export PATH=/usr/bin:/bin:/usr/sbin:/sbin
16say() { echo "== cosmic-qemu :: $*" | tee -a /dev/console /dev/ttyS0 2>/dev/null; }
17dump() { echo "---- $1 ----" > /dev/ttyS0 2>/dev/null; cat "$1" > /dev/ttyS0 2>/dev/null; }
18alive() { s=$(sed -n 's/^State:[[:space:]]*//p' /proc/$1/status 2>/dev/null); case "$s" in ""|Z*) return 1;; *) return 0;; esac; }
19
20say "arrancando (pid $$)"
21
22# El getty ejecuta este script SIN ambiente heredable, así que las perillas de la imagen viajan como
23# FICHERO —mismo truco que `/etc/gnome-mode`—. Hoy sólo `COSMIC_MODE` (bare|session).
24[ -r /etc/cosmic-mode ] && . /etc/cosmic-mode
25
26# ── FS virtuales y directorios de runtime ───────────────────────────────────────────────────────
27mount -t proc none /proc 2>/dev/null
28mount -t sysfs none /sys 2>/dev/null
29mount -t devtmpfs none /dev 2>/dev/null
30mkdir -p /dev/pts /dev/shm && mount -t devpts none /dev/pts 2>/dev/null && mount -t tmpfs none /dev/shm 2>/dev/null
31# XDG_RUNTIME_DIR es OBLIGATORIO para Wayland: ahí viven el socket del compositor y los de los
32# clientes. Sin él el compositor arranca y nadie puede conectarse — y el error que da es del cliente,
33# no del compositor, que es lo que hace confuso el diagnóstico.
34export XDG_RUNTIME_DIR=/run/user/0
35mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR"
36# …y COMPROBARLO, porque el primer arranque falló justo acá y el error salía del sitio equivocado:
37# dbus decía «XDG_RUNTIME_DIR /run/user/0 not available» y eso se lee como un problema de dbus. El
38# `mkdir -p` de arriba no garantiza nada si /run no es escribible o si algo lo montó encima.
39if [ -d "$XDG_RUNTIME_DIR" ] && [ -w "$XDG_RUNTIME_DIR" ]; then
40 say "XDG_RUNTIME_DIR OK ($XDG_RUNTIME_DIR)"
41else
42 say "!! XDG_RUNTIME_DIR NO existe/no escribible — /run es: $(ls -ld /run 2>&1)"
43 say " montando tmpfs en /run"
44 mount -t tmpfs none /run 2>/dev/null
45 mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR"
46 [ -w "$XDG_RUNTIME_DIR" ] && say " ✓ ahora sí" || say " ✗ sigue sin andar"
47fi
48mkdir -p /root/.config /root/.cache /var/lib/dbus /run/dbus /tmp && chmod 1777 /tmp
49export HOME=/root SHELL=/bin/bash # PATH ya salió arriba, antes del primer mkdir
50
51say "kernel $(uname -r) · drm: $(ls /dev/dri 2>/dev/null | tr '\n' ' ')"
52
53# ── Render por software: la VM no tiene GPU ─────────────────────────────────────────────────────
54# Las cuatro perillas vienen pagas de las campañas KDE y GNOME. `GBM_ALWAYS_SOFTWARE` + el driver
55# `kms_swrast` son lo que hace que mesa acepte crear un device GBM sobre virtio-gpu sin acelerador;
56# `LP_NUM_THREADS=1` evita que llvmpipe se coma los cuatro vcpus y deje al compositor sin planificar.
57export GBM_ALWAYS_SOFTWARE=1
58export LIBGL_ALWAYS_SOFTWARE=1
59export MESA_LOADER_DRIVER_OVERRIDE=kms_swrast
60export LP_NUM_THREADS=1
61# COSMIC pinta con tiny-skia (libcosmic entra sin `wgpu`), pero el compositor SÍ usa EGL/GL por
62# smithay para componer, así que las de arriba siguen haciendo falta.
63export RUST_BACKTRACE=1
64export RUST_LOG="${RUST_LOG:-info}"
65
66# ── seatd: quién le da los devices al compositor ────────────────────────────────────────────────
67# smithay entra con `backend_session_libseat`, y libseat tiene dos backends: hablar con un `seatd`
68# corriendo, o —si el proceso ya es root— abrir los devices él mismo (`builtin`). Acá se levanta
69# seatd de verdad en vez de usar el builtin, porque es el camino que también sirve fuera de QEMU y
70# porque deja un log donde mirar. Si no sube, se cae al builtin: mejor un escritorio con andamio que
71# ninguno, pero se dice en el serial para que no pase por bueno.
72if [ -x /usr/bin/seatd ]; then
73 /usr/bin/seatd -g video >/tmp/seatd.log 2>&1 &
74 SEATD_PID=$!
75 sleep 1
76 if alive $SEATD_PID && [ -S /run/seatd.sock ]; then
77 say "seatd OK (pid $SEATD_PID, /run/seatd.sock)"
78 else
79 say "!! seatd no levantó — caigo a LIBSEAT_BACKEND=builtin (somos root)"; dump /tmp/seatd.log
80 export LIBSEAT_BACKEND=builtin
81 fi
82else
83 say "!! sin seatd — LIBSEAT_BACKEND=builtin"
84 export LIBSEAT_BACKEND=builtin
85fi
86
87# ── bus de SISTEMA ──────────────────────────────────────────────────────────────────────────────
88# cosmic-comp habla logind por zbus (sesión, inhibidores) y el bus de sesión lo abre después
89# `start-cosmic` con dbus-run-session. El de sistema hay que levantarlo acá.
90if [ -x /usr/bin/dbus-daemon ]; then
91 # ⚠ El mkdir de /run/dbus va ACÁ, no arriba con los demás. El primer arranque en modo bare falló
92 # con «Failed to bind socket /run/dbus/system_bus_socket: No such file or directory» aunque el
93 # directorio se creaba al principio del script: entre un punto y otro arje-zero sigue montando, y
94 # un tmpfs sobre /run se lleva puesto lo que hubiera. Crear justo antes de usar es la única forma
95 # de no depender de ese orden. Mismo motivo que el segundo mkdir de XDG_RUNTIME_DIR.
96 mkdir -p /run/dbus /var/lib/dbus
97 [ -f /var/lib/dbus/machine-id ] || dbus-uuidgen > /var/lib/dbus/machine-id 2>/dev/null
98 [ -f /etc/machine-id ] || cp /var/lib/dbus/machine-id /etc/machine-id 2>/dev/null
99 dbus-daemon --system --fork >/tmp/dbus-system.log 2>&1
100 sleep 1
101 [ -S /run/dbus/system_bus_socket ] && say "bus de sistema OK" || { say "!! sin bus de sistema:"; dump /tmp/dbus-system.log; }
102else
103 say "!! sin dbus-daemon"
104fi
105
106# ── login1: el shim de arje ─────────────────────────────────────────────────────────────────────
107# Mismo eslabón que en GNOME y KDE. cosmic-comp pregunta por su sesión; quien contesta
108# `org.freedesktop.login1` acá es arje-logind-compat. Se ESPERA el nombre, no el pid: un pid no es un
109# servicio, y esperar sólo al final deja pasar la carrera (lección de gnome-start, 2026-08-03).
110if [ -x /usr/bin/arje-logind-compat ]; then
111 ARJE_LOGIN_STATE=1 /usr/bin/arje-logind-compat >/tmp/logind-compat.log 2>&1 &
112 say "arje-logind-compat lanzado (pid $!)"
113 i=0
114 while [ $i -lt 20 ]; do
115 if dbus-send --system --dest=org.freedesktop.DBus --print-reply /org/freedesktop/DBus \
116 org.freedesktop.DBus.NameHasOwner string:org.freedesktop.login1 2>/dev/null | grep -q "boolean true"; then
117 say "bus: org.freedesktop.login1 OK"; break
118 fi
119 i=$((i+1)); sleep 0.5
120 done
121 [ $i -lt 20 ] || { say "!! login1 NO apareció en 10s:"; dump /tmp/logind-compat.log; }
122else
123 say "!! sin arje-logind-compat — cosmic-comp puede no hallar sesión"
124fi
125
126# ── COSMIC ──────────────────────────────────────────────────────────────────────────────────────
127export XDG_CURRENT_DESKTOP=COSMIC XDG_SESSION_DESKTOP=COSMIC XDG_SESSION_TYPE=wayland
128export XDG_SESSION_ID=1 XDG_SEAT=seat0 XDG_VTNR=1
129export XDG_DATA_DIRS=/usr/share XDG_CONFIG_DIRS=/etc/xdg
130
131# Y otra vez JUSTO ANTES de arrancar, no sólo al principio: entre el getty y este punto pasan varios
132# segundos en los que arje-zero sigue montando cosas, y un tmpfs sobre /run se lleva puesto el
133# directorio sin avisar. Comprobar al principio y usar al final es cómo se cuela una carrera.
134mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR"
135[ -w "$XDG_RUNTIME_DIR" ] || say "!! XDG_RUNTIME_DIR se perdió entre el arranque y ahora"
136
137# ── FONDO: color liso, porque la imagen por defecto NO SE PUEDE EMPAQUETAR ────────────────────
138# El default de cosmic-bg apunta a `/usr/share/backgrounds/cosmic/orion_nebula_nasa_heic0601a.jpg`,
139# que vive en el repo `cosmic-wallpapers`… en **git-lfs**: el tarball de GitHub pesa 20 KB y trae
140# punteros, no imágenes. O sea que la receta obvia produciría un paquete de ficheros de texto.
141# Mientras eso no se resuelva, se escribe la config de USUARIO con una fuente `Color`, que
142# cosmic-bg soporta de fábrica. Va acá y no en el artefacto a propósito: es política de esta
143# imagen de prueba, no algo que el paquete prometa.
144if [ ! -f /usr/share/backgrounds/cosmic/orion_nebula_nasa_heic0601a.jpg ]; then
145 mkdir -p "$HOME/.config/cosmic/com.system76.CosmicBackground/v1"
146 cat > "$HOME/.config/cosmic/com.system76.CosmicBackground/v1/all" <<'RON'
147(
148 output: "all",
149 source: Color(Single((0.13, 0.29, 0.53))),
150 filter_by_theme: true,
151 rotation_frequency: 3600,
152 filter_method: Lanczos,
153 scaling_mode: Zoom,
154 sampling_method: Alphanumeric,
155)
156RON
157 say "fondo: color liso (el wallpaper por defecto vive en git-lfs y no se empaqueta)"
158fi
159
160# ── SEMILLA DE CONFIG DE qalc, SIN LA CUAL LA CALCULADORA DEL LANZADOR NUNCA ANDA ────────────────
161# En el primer arranque `qalc` hace una PREGUNTA interactiva («¿activar autocalc?») antes de leer
162# nada. El plugin `calc` de pop-launcher le escribe la expresión y cierra el pipe, así que la
163# expresión se consume como respuesta, no es un sí/no válido, y qalc **no llega a guardar config** ⇒
164# la próxima vez vuelve a preguntar. O sea que sin sembrar esto no falla una vez: falla SIEMPRE.
165# Es política de la imagen, no del paquete —igual que el color de fondo—: una distro de verdad lo
166# pondría en el esqueleto de /etc/skel.
167if [ ! -f "$HOME/.config/qalculate/qalc.cfg" ]; then
168 mkdir -p "$HOME/.config/qalculate"
169 printf '[General]\ncalculate_as_you_type=0\n' > "$HOME/.config/qalculate/qalc.cfg"
170 say "qalc: config sembrada (si no, la pregunta de primer arranque se come la cuenta)"
171fi
172
173# ── MODO: bare (default) o session ──────────────────────────────────────────────────────────────
174# `cosmic-session` llama a `cosmic-settings-daemon` con
175# `.expect("failed to start settings daemon")` (src/main.rs:255) y PANICKEA si no está — no es
176# opcional ni está detrás de una feature.
177#
178# ⚠ **DESACTUALIZADO Y CORREGIDO (2026-08-05)**: acá decía que ese daemon «todavía no se puede
179# construir acá (arrastra pipewire)». Es falso desde hace tiempo — `cosmic-settings-daemon` está
180# sellado y corre, y `pipewire` está en la imagen. Se deja el rastro porque el comentario viejo
181# mandaba a buscar un bloqueo que ya no existe.
182#
183# Lo que SÍ falta, y es otra cosa: **nadie ARRANCA el demonio de pipewire**. `/usr/bin/pipewire` y
184# `pipewire-pulse` están instalados, pero `ps ax | grep pipewire` en la sesión no devuelve nada, y
185# este script no lo lanza. Consecuencia medida: el portal acepta
186# `org.freedesktop.portal.ScreenCast.CreateSession` y devuelve su objeto Request, pero **no puede
187# haber stream** porque no hay demonio con quien negociarlo. Sin systemd de usuario, lanzarlo es
188# tarea de este script. (Para audio además falta `wireplumber`, que ni siquiera está construido.)
189#
190# El modo `bare` es la otra mitad que upstream ya soporta (`make install-bare-session`): el
191# compositor se lanza SOLO y los clientes se levantan a mano contra su WAYLAND_DISPLAY. Sirve para lo
192# que hace falta ahora, que es saber si el escritorio PINTA — y separa las dos preguntas: «¿compone?»
193# es del compositor, «¿arranca la sesión?» es del gestor.
194#
195# COSMIC_MODE=session para probar el camino de producción cuando el daemon exista.
196if [ "${COSMIC_MODE:-bare}" = session ]; then
197 say "lanzando cosmic-session vía start-cosmic (modo session)"
198 /bin/bash /usr/bin/start-cosmic --in-login-shell >/tmp/cosmic-session.log 2>&1 &
199 CS_PID=$!
200else
201 say "lanzando cosmic-comp SOLO (modo bare; cosmic-session necesita settings-daemon)"
202 dbus-daemon --session --fork --print-address=1 >/tmp/dbus-session-addr 2>/tmp/dbus-session.log
203 DBUS_SESSION_BUS_ADDRESS=$(cat /tmp/dbus-session-addr 2>/dev/null)
204 [ -n "$DBUS_SESSION_BUS_ADDRESS" ] && export DBUS_SESSION_BUS_ADDRESS && say "bus de sesión OK" \
205 || { say "!! sin bus de sesión:"; dump /tmp/dbus-session.log; }
206 /usr/bin/cosmic-comp >/tmp/cosmic-session.log 2>&1 &
207 CS_PID=$!
208fi
209
210# ── Reporte: el socket, no el pid ───────────────────────────────────────────────────────────────
211# «Un pid no es un servicio» — la lección que costó dos iteraciones en GNOME. Lo que dice que el
212# compositor está VIVO es que exista su socket Wayland, no que el proceso no haya muerto todavía.
213i=0; WD=""
214while [ $i -lt 60 ]; do
215 WD=$(ls "$XDG_RUNTIME_DIR" 2>/dev/null | grep -m1 '^wayland-[0-9]$')
216 if [ -n "$WD" ]; then
217 say "compositor OK ($WD) — cosmic-comp ES el display server"
218 break
219 fi
220 alive $CS_PID || { say "!! la sesión MURIÓ antes de crear el socket:"; dump /tmp/cosmic-session.log; break; }
221 i=$((i+1)); sleep 1
222done
223[ -n "$WD" ] || say "!! sin socket wayland en ${i}s:"
224[ -n "$WD" ] || dump /tmp/cosmic-session.log
225
226# ── Los clientes, contra el socket que el compositor haya elegido ───────────────────────────────
227# En modo bare no hay quien los levante: `cosmic-session` es el que normalmente hace esto, y por eso
228# cada uno recibe WAYLAND_DISPLAY explícito. Se lanza sólo lo que ESTÉ instalado — la cola COSMIC se
229# construye por partes, y un `[ -x ]` de más es más barato que un arranque perdido.
230if [ -n "$WD" ] && [ "${COSMIC_MODE:-bare}" != session ]; then
231 export WAYLAND_DISPLAY="$WD"
232
233 for c in cosmic-bg cosmic-panel cosmic-osd cosmic-notifications cosmic-app-library cosmic-launcher cosmic-idle; do
234 if [ -x "/usr/bin/$c" ]; then
235 "/usr/bin/$c" >"/tmp/$c.log" 2>&1 &
236 say "cliente $c lanzado (pid $!)"
237 sleep 1
238 fi
239 done
240fi
241
242# Estado periódico + el log completo al final de cada tramo: el dato que importa no es que siga vivo
243# sino QUÉ componentes levantó la sesión.
244n=0
245while [ $n -lt 12 ]; do
246 sleep 15; n=$((n+1))
247 # `pgrep -c` de busybox devolvía 0 con el compositor VIVO y su socket abierto — o sea que el
248 # contador mentía. Se cuentan los /proc/*/comm, que es el dato del kernel y no una utilería.
249 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' ' ')"
250 [ $n = 2 ] && { say "log del compositor (primer tramo)"; dump /tmp/cosmic-session.log; for l in /tmp/cosmic-bg.log /tmp/cosmic-panel.log; do [ -s "$l" ] && dump "$l"; done; }
251done
252
253say "fin de la ventana de observación"
254dump /tmp/cosmic-session.log
255exec /bin/sh