diff --git a/README.md b/README.md index b0c2537..9c106dc 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ sessiond-hooks --config PATH [--log-target stderr|syslog] ```dhall let Hooks = env:SESSIOND_HOOKS -\(ctx : Hooks.Context) +in \(ctx : Hooks.Context) -> { init : Text , args : List Text , restart : Hooks.Restart diff --git a/dhall/tests/runit.dhall b/dhall/tests/runit.dhall index 1c2b129..370d205 100644 --- a/dhall/tests/runit.dhall +++ b/dhall/tests/runit.dhall @@ -3,7 +3,7 @@ -- which the free-form `args` list expresses directly. let Hooks = env:SESSIOND_HOOKS -λ(ctx : Hooks.Context) → +in λ(ctx : Hooks.Context) → { init = "runsvdir" , args = [ "${ctx.home}/service" ] , restart = Hooks.defaultRestart diff --git a/dhall/tests/wrong-shape.dhall b/dhall/tests/wrong-shape.dhall index 52409c3..88d033d 100644 --- a/dhall/tests/wrong-shape.dhall +++ b/dhall/tests/wrong-shape.dhall @@ -3,5 +3,5 @@ -- so this must fail to typecheck rather than producing a bad Config. let Hooks = env:SESSIOND_HOOKS -λ(ctx : Hooks.Context) → +in λ(ctx : Hooks.Context) → { init = "/usr/bin/dinit", args = "--user" } diff --git a/nix/finix-vm.nix b/nix/finix-vm.nix index 994ebdd..663179f 100644 --- a/nix/finix-vm.nix +++ b/nix/finix-vm.nix @@ -18,7 +18,7 @@ in hooks.dinit.configFile = pkgs.writeText "sessiond-hooks-dinit.dhall" '' let Hooks = env:SESSIOND_HOOKS - \(ctx : Hooks.Context) -> + in \(ctx : Hooks.Context) -> { init = "${pkgs.dinit}/bin/dinit" , args = [ "--user", "--services-dir", "''${ctx.home}/.config/dinit.d" ] , restart = Hooks.restart.always diff --git a/src/main.rs b/src/main.rs index ba8fdc4..6e77d28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ mod session_management_v2 { mod session_core_v1 { hyprwire::include_protocol!("session_core_v1"); pub use client::*; + pub use spec::*; } mod config; @@ -20,12 +21,9 @@ use session_management_v2::{session_management_session, session_manager}; use std::collections::BTreeMap; use std::os::fd::AsRawFd; use std::os::unix::process::CommandExt; -use std::{env, fs, path, process, time}; +use std::{env, path, process, time}; use uzers::os::unix::UserExt; -const DEFAULT_UID_MIN: u32 = 1000; -const DEFAULT_UID_MAX: u32 = 60000; - #[derive(Parser)] #[command(version, about = "Launch and supervise a per-user session hook")] struct Cli { @@ -49,6 +47,7 @@ struct Session { id: u32, object: session_core_session::SessionCoreSession, uid: u32, + class: Option, } struct ManagedSession { @@ -231,47 +230,6 @@ struct SessiondHooks { loop_handle: calloop::LoopHandle<'static, Self>, } -fn users_uid_range() -> (u32, u32) { - let mut uid_min = DEFAULT_UID_MIN; - let mut uid_max = DEFAULT_UID_MAX; - - let Ok(login_defs) = fs::read_to_string("/etc/login.defs") else { - return (uid_min, uid_max); - }; - - for line in login_defs.lines() { - let mut parts = line.split_whitespace(); - let Some(key) = parts.next() else { - continue; - }; - let Some(value) = parts.next() else { - continue; - }; - - match key { - "UID_MIN" => uid_min = value.parse().unwrap_or(uid_min), - "UID_MAX" => uid_max = value.parse().unwrap_or(uid_max), - _ => {} - } - } - - (uid_min, uid_max) -} - -fn is_regular_user(uid: u32) -> bool { - let (uid_min, uid_max) = users_uid_range(); - if !(uid_min..=uid_max).contains(&uid) { - return false; - } - - let Some(user) = uzers::get_user_by_uid(uid) else { - return false; - }; - - let shell = user.shell(); - shell != "/usr/sbin/nologin" && shell != "/sbin/nologin" && shell != "/bin/false" -} - impl hyprwire::Dispatch for SessiondHooks { fn event( &mut self, @@ -300,6 +258,7 @@ impl hyprwire::Dispatch for SessiondHo id: session_id, object: session_object, uid: 0, + class: None, }) } session_core_manager::Event::Done => {} @@ -374,35 +333,41 @@ impl hyprwire::Dispatch for SessiondHo object: &session_core_session::SessionCoreSession, event: ::Event<'_>, ) { + let Some(session) = self + .sessions + .iter_mut() + .find(|session| session.object == *object) + else { + return; + }; match event { session_core_session::Event::Uid { uid } => { - let Some(session) = self - .sessions - .iter_mut() - .find(|session| session.object == *object) - else { - return; - }; - session.uid = uid; - log::info!(session_id = session.id, uid; "session belongs to uid"); - - if !is_regular_user(uid) { - log::info!(uid; "uid is not a regular login user"); + } + session_core_session::Event::Class { class } => { + session.class = Some(class); + } + session_core_session::Event::Done => { + if session + .class + .is_none_or(|class| class != session_core_v1::SessionClass::User) + { return; } + log::info!(session_id = session.id, uid = session.uid; "session belongs to uid"); + if self .managed_sessions .iter() - .any(|session| session.uid == uid) + .any(|managed_session| managed_session.uid == session.uid) { - log::info!(uid; "uid already has a managed hook session"); + log::info!(uid = session.uid; "uid already has a managed hook session"); return; } let Some(session_manager) = self.session_manager.as_ref() else { - log::error!(uid; "cannot create managed session: session manager is not bound"); + log::error!(uid = session.uid; "cannot create managed session: session manager is not bound"); return; }; @@ -428,8 +393,9 @@ impl hyprwire::Dispatch for SessiondHo } }; - let Some(session_object) = session_manager.send_create_session::(uid) else { - log::error!(uid; "failed to create managed session: protocol returned no object"); + let Some(session_object) = session_manager.send_create_session::(session.uid) + else { + log::error!(uid = session.uid; "failed to create managed session: protocol returned no object"); return; }; session_object.send_class(session_management_v2::SessionClass::Manager); @@ -437,7 +403,7 @@ impl hyprwire::Dispatch for SessiondHo self.managed_sessions.push(ManagedSession { id: 0, - uid, + uid: session.uid, object: session_object, envs: BTreeMap::from([ (