← luna

Commit 6c27cca

7
files
+122
added
-4
deleted
@@ -170,7 +170,8 @@ Po instalacji sesja „Luna” pojawia się w menedżerze logowania (`share/wayl
170 170 Na sprzęcie Selene przejmuje ekran przez DRM/KMS — trzeba **wylogować bieżący pulpit** (wolny
171 171 master DRM) i mieć dostęp do `/dev/input` (grupa `input`) dla wejścia. Testowo, obok działającej
172 172 sesji, można uruchomić `SELENE_BACKEND=nested selene` (okno na obecnym pulpicie) i podłączyć
173 -`luna-shell`. Szczegóły: `de/README.md`.
173 +`luna-shell`. Gdy sesja od razu wraca do menedżera logowania, sprawdź `XDG_RUNTIME_DIR` —
174 +Selene i `luna-session` wybierają go awaryjnie same. Szczegóły: `de/README.md`.
174 175
175 176 ## Budowanie własnej aplikacji na libLunie
176 177
@@ -102,6 +102,11 @@ XDG_RUNTIME_DIR="$PWD/.selene-runtime" cargo run -p selene
102 102 # → "Selene compositor started (wayland-0)" + gniazdo wayland-0 w katalogu runtime
103 103 ```
104 104
105 +`XDG_RUNTIME_DIR` zwykle ustawia menedżer sesji. Gdy go brak (np. `pam_systemd` nie
106 +założył sesji), Selene wybiera katalog sam: najpierw `/run/user/<uid>`, a gdy ten nie
107 +istnieje — tworzy prywatny katalog `0700` pod `$TMPDIR`. Skrypt `luna-session` robi ten
108 +sam wybór wcześniej, żeby zmienną dziedziczyli także klienci.
109 +
105 110 Tryb nested (domyślny) uruchamia okno na nadrzędnym kompozytorze. **Realne wyjście DRM**
106 111 (gdy masz wolnego mastera, np. z osobnej konsoli):
107 112
@@ -20,4 +20,4 @@ libluna-platform.workspace = true
20 20 wayland-server = "0.31"
21 21 wayland-protocols = { version = "0.32", features = ["server", "unstable"] }
22 22 wayland-protocols-wlr = { version = "0.3", features = ["server"] }
23 -rustix = { version = "0.38", features = ["event", "fs", "time", "mm"] }
23 +rustix = { version = "0.38", features = ["event", "fs", "time", "mm", "process"] }
@@ -21,6 +21,8 @@ mod state;
21 21
22 22 use std::error::Error;
23 23 use std::os::fd::{AsRawFd, BorrowedFd};
24 +use std::os::unix::fs::MetadataExt;
25 +use std::path::{Path, PathBuf};
24 26 use std::sync::Arc;
25 27
26 28 use libluna_core::Event;
@@ -81,6 +83,7 @@ fn main() -> Result<(), Box<dyn Error>> {
81 83 handle.create_global::<State, wl_seat::WlSeat, ()>(1, ());
82 84
83 85 // Gniazdo nasłuchu: `wayland-0`, `wayland-1`, …
86 + ensure_runtime_dir()?;
84 87 let socket =
85 88 ListeningSocket::bind_auto("wayland", 0..33).map_err(|e| format!("bind socket: {e:?}"))?;
86 89 println!(
@@ -195,3 +198,92 @@ fn main() -> Result<(), Box<dyn Error>> {
195 198 display.flush_clients()?;
196 199 }
197 200 }
201 +
202 +/// Zapewnia, że `XDG_RUNTIME_DIR` wskazuje prywatny katalog użytkownika, zanim
203 +/// zwiążemy gniazdo Wayland.
204 +///
205 +/// Menedżer sesji (logind/`pam_systemd`) normalnie ustawia tę zmienną. Gdy jej brak
206 +/// lub jest bezużyteczna (np. `pam_systemd` nie zdołał założyć sesji i zmienna nie
207 +/// trafiła do środowiska), `ListeningSocket::bind_auto` kończy się błędem
208 +/// `RuntimeDirNotSet`, a kompozytor wychodzi natychmiast — co z zewnątrz wygląda jak
209 +/// „sesja się nie uruchomiła”. Dlatego wybieramy `/run/user/<uid>`, a gdy ono nie
210 +/// istnieje — tworzymy własny katalog `0700` pod `$TMPDIR`.
211 +fn ensure_runtime_dir() -> Result<(), Box<dyn Error>> {
212 + let uid = rustix::process::getuid().as_raw() as u32;
213 +
214 + if let Some(dir) = std::env::var_os("XDG_RUNTIME_DIR") {
215 + if is_private_dir_owned_by(&PathBuf::from(dir), uid) {
216 + return Ok(());
217 + }
218 + }
219 +
220 + let run_user = PathBuf::from(format!("/run/user/{uid}"));
221 + let dir = if is_private_dir_owned_by(&run_user, uid) {
222 + run_user
223 + } else {
224 + let base = std::env::var_os("TMPDIR")
225 + .map(PathBuf::from)
226 + .filter(|p| p.is_absolute())
227 + .unwrap_or_else(|| PathBuf::from("/tmp"));
228 + let dir = base.join(format!("selene-runtime-{uid}"));
229 + match rustix::fs::mkdir(&dir, rustix::fs::Mode::RWXU) {
230 + Ok(()) | Err(rustix::io::Errno::EXIST) => {}
231 + Err(e) => return Err(format!("mkdir {}: {e}", dir.display()).into()),
232 + }
233 + if !is_private_dir_owned_by(&dir, uid) {
234 + return Err(format!(
235 + "runtime dir {} nie należy do uid {uid} lub jest dostępny dla grupy/innych",
236 + dir.display()
237 + )
238 + .into());
239 + }
240 + dir
241 + };
242 +
243 + println!(
244 + "{} {}",
245 + libluna_i18n::tr("XDG_RUNTIME_DIR not set, using"),
246 + dir.display()
247 + );
248 + std::env::set_var("XDG_RUNTIME_DIR", &dir);
249 + Ok(())
250 +}
251 +
252 +/// Czy `path` to istniejący, bezwzględny katalog należący do `uid`, bez dostępu dla
253 +/// grupy i innych użytkowników (tryb `0700`)?
254 +fn is_private_dir_owned_by(path: &Path, uid: u32) -> bool {
255 + path.is_absolute()
256 + && std::fs::metadata(path)
257 + .map(|m| m.is_dir() && m.uid() == uid && m.mode() & 0o077 == 0)
258 + .unwrap_or(false)
259 +}
260 +
261 +#[cfg(test)]
262 +mod runtime_dir_tests {
263 + use super::*;
264 + use std::os::unix::fs::PermissionsExt as _;
265 +
266 + #[test]
267 + fn private_dir_owned_by_uid_is_accepted() {
268 + let uid = rustix::process::getuid().as_raw() as u32;
269 + let dir = std::env::temp_dir().join(format!("selene-rdir-{}", std::process::id()));
270 + std::fs::create_dir_all(&dir).unwrap();
271 + std::fs::set_permissions(&dir, std::fs::Permissions::from_mode(0o700)).unwrap();
272 +
273 + assert!(
274 + is_private_dir_owned_by(&dir, uid),
275 + "katalog 0700 naszego uid"
276 + );
277 + // Obcy uid: nie jest nasz.
278 + assert!(!is_private_dir_owned_by(&dir, uid.wrapping_add(1)));
279 + // Zbyt otwarte uprawnienia: grupa/inny mają dostęp.
280 + std::fs::set_permissions(&dir, std::fs::Permissions::from_mode(0o755)).unwrap();
281 + assert!(!is_private_dir_owned_by(&dir, uid));
282 + // Nieistniejąca ścieżka.
283 + assert!(!is_private_dir_owned_by(&dir.join("missing"), uid));
284 + // Ścieżka względna nie nadaje się na `XDG_RUNTIME_DIR`.
285 + assert!(!is_private_dir_owned_by(Path::new("relative/dir"), uid));
286 +
287 + std::fs::remove_dir_all(&dir).ok();
288 + }
289 +}
@@ -10,6 +10,14 @@ export XDG_CURRENT_DESKTOP=Luna
10 10 export XDG_SESSION_TYPE=wayland
11 11 export XDG_SESSION_DESKTOP=Luna
12 12
13 +# XDG_RUNTIME_DIR normalnie ustawia menedżer sesji (logind/pam_systemd). Gdy go brak
14 +# (np. pam_systemd nie zdołał założyć sesji), wskazujemy katalog per-uid, żeby Selene
15 +# i klienci mieli gdzie założyć gniazdo Wayland. Selene sam waliduje ten katalog.
16 +if [ -z "${XDG_RUNTIME_DIR:-}" ] && [ -d "/run/user/$(id -u)" ]; then
17 + XDG_RUNTIME_DIR="/run/user/$(id -u)"
18 + export XDG_RUNTIME_DIR
19 +fi
20 +
13 21 : "${SELENE_BACKEND:=drm}"
14 22 export SELENE_BACKEND
15 23
@@ -54,6 +54,9 @@ sum — te wpisuje dopiero skrypt, bo zależą od konkretnego commita/taga.
54 54
55 55 - `Selene` to kompozytor Wayland; wpis sesji startuje na DRM/KMS (`SELENE_BACKEND=drm`).
56 56 Gdy master DRM trzyma inna sesja, Selene **bezpiecznie** spada do wyjścia nested.
57 +- `XDG_RUNTIME_DIR` pochodzi od menedżera sesji; gdy go brak (np. `pam_systemd` nie założył
58 + sesji), `luna-session` i sam Selene wybierają katalog awaryjnie (`/run/user/<uid>`, dalej
59 + prywatny `0700` pod `$TMPDIR`).
57 60 - Wejście w trybie DRM (evdev) to kolejny krok — póki go nie ma, sesja jest „podglądowa”.
58 61 - `selene` nie deklaruje zależności od pakietu `libluna`, bo linkuje toolkit statycznie
59 62 (Rust) — zależność nie jest potrzebna w runtime.
@@ -1,14 +1,23 @@
1 1 #!/bin/sh
2 2 # Sesja środowiska Luna — uruchamia kompozytor Selene.
3 3 #
4 -# Domyślnie startujemy na DRM/KMS. Gdy master DRM trzyma inna sesja, Selene sam
5 -# zgłosi to i spadnie do wyjścia nested — ustaw wtedy SELENE_BACKEND=nested.
4 +# Domyślnie startujemy na DRM/KMS (własny ekran, bez nadrzędnego kompozytora).
5 +# Gdy master DRM trzyma inna sesja, Selene sam to zgłosi i spadnie do wyjścia
6 +# nested — ustaw wtedy SELENE_BACKEND=nested (albo wyloguj bieżącą sesję).
6 7 set -eu
7 8
8 9 export XDG_CURRENT_DESKTOP=Luna
9 10 export XDG_SESSION_TYPE=wayland
10 11 export XDG_SESSION_DESKTOP=Luna
11 12
13 +# XDG_RUNTIME_DIR normalnie ustawia menedżer sesji (logind/pam_systemd). Gdy go brak
14 +# (np. pam_systemd nie zdołał założyć sesji), wskazujemy katalog per-uid, żeby Selene
15 +# i klienci mieli gdzie założyć gniazdo Wayland. Selene sam waliduje ten katalog.
16 +if [ -z "${XDG_RUNTIME_DIR:-}" ] && [ -d "/run/user/$(id -u)" ]; then
17 + XDG_RUNTIME_DIR="/run/user/$(id -u)"
18 + export XDG_RUNTIME_DIR
19 +fi
20 +
12 21 : "${SELENE_BACKEND:=drm}"
13 22 export SELENE_BACKEND
14 23