Implement kiosk mode for cosmic-greeter

This commit is contained in:
Jeremy Soller 2024-02-06 09:25:48 -07:00 • committed by Victoria Brekenfeld
parent 54f2d2c67e
commit ec822e421f
2 changed files with 65 additions and 20 deletions

View file

@ -9,7 +9,7 @@ use smithay::{
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::{ffi::OsString, sync::Arc}; use std::{env, ffi::OsString, process, sync::Arc};
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use crate::wayland::handlers::compositor::client_compositor_state; use crate::wayland::handlers::compositor::client_compositor_state;
@ -56,6 +56,18 @@ fn main() -> Result<()> {
// potentially tell the session we are setup now // potentially tell the session we are setup now
session::setup_socket(event_loop.handle(), &state)?; session::setup_socket(event_loop.handle(), &state)?;
let mut args = env::args().skip(1);
let mut child_opt = if let Some(exec) = args.next() {
// Run command in kiosk mode
let mut command = process::Command::new(&exec);
command.args(args);
command.envs(session::get_env(&state)?);
info!("Running {:?}", exec);
Some(command.spawn()?)
} else {
None
};
if let Err(err) = theme::watch_theme(event_loop.handle()) { if let Err(err) = theme::watch_theme(event_loop.handle()) {
warn!(?err, "Failed to watch theme"); warn!(?err, "Failed to watch theme");
} }
@ -83,8 +95,36 @@ fn main() -> Result<()> {
// send out events // send out events
let _ = state.common.display_handle.flush_clients(); let _ = state.common.display_handle.flush_clients();
// check if kiosk child is running
if let Some(ref mut child) = child_opt {
match child.try_wait() {
// Kiosk child exited with status
Ok(Some(exit_status)) => {
info!("Command exited with status {:?}", exit_status);
match exit_status.code() {
// Exiting with the same status as the kiosk child
Some(code) => process::exit(code),
// The kiosk child exited with signal, exiting with error
None => process::exit(1),
}
}
// Command still running
Ok(None) => {}
// Kiosk child disappeared, exiting with error
Err(err) => {
warn!(?err, "Failed to wait for command");
process::exit(1);
}
}
}
})?; })?;
// kill kiosk child if loop exited
if let Some(mut child) = child_opt {
let _ = child.kill();
}
// drop eventloop & state before logger // drop eventloop & state before logger
std::mem::drop(event_loop); std::mem::drop(event_loop);
std::mem::drop(state); std::mem::drop(state);

View file

@ -59,19 +59,7 @@ unsafe fn set_cloexec(fd: RawFd) -> rustix::io::Result<()> {
rustix::io::fcntl_setfd(fd, flags | rustix::io::FdFlags::CLOEXEC) rustix::io::fcntl_setfd(fd, flags | rustix::io::FdFlags::CLOEXEC)
} }
pub fn setup_socket(handle: LoopHandle<State>, state: &State) -> Result<()> { pub fn get_env(state: &State) -> Result<HashMap<String, String>> {
if let Ok(fd_num) = std::env::var("COSMIC_SESSION_SOCK") {
if let Ok(fd) = fd_num.parse::<RawFd>() {
let mut session_socket = match unsafe { set_cloexec(fd) } {
// CLOEXEC worked and we can startup with session IPC
Ok(_) => unsafe { UnixStream::from_raw_fd(fd) },
// CLOEXEC didn't work, something is wrong with the fd, just close it
Err(err) => {
unsafe { rustix::io::close(fd) };
return Err(err).with_context(|| "Failed to setup session socket");
}
};
let mut env = HashMap::new(); let mut env = HashMap::new();
env.insert( env.insert(
String::from("WAYLAND_DISPLAY"), String::from("WAYLAND_DISPLAY"),
@ -91,6 +79,23 @@ pub fn setup_socket(handle: LoopHandle<State>, state: &State) -> Result<()> {
{ {
env.insert(String::from("DISPLAY"), format!(":{}", display)); env.insert(String::from("DISPLAY"), format!(":{}", display));
} }
Ok(env)
}
pub fn setup_socket(handle: LoopHandle<State>, state: &State) -> Result<()> {
if let Ok(fd_num) = std::env::var("COSMIC_SESSION_SOCK") {
if let Ok(fd) = fd_num.parse::<RawFd>() {
let mut session_socket = match unsafe { set_cloexec(fd) } {
// CLOEXEC worked and we can startup with session IPC
Ok(_) => unsafe { UnixStream::from_raw_fd(fd) },
// CLOEXEC didn't work, something is wrong with the fd, just close it
Err(err) => {
unsafe { rustix::io::close(fd) };
return Err(err).with_context(|| "Failed to setup session socket");
}
};
let env = get_env(state)?;
let message = serde_json::to_string(&Message::SetEnv { variables: env }) let message = serde_json::to_string(&Message::SetEnv { variables: env })
.with_context(|| "Failed to encode environment variables into json")?; .with_context(|| "Failed to encode environment variables into json")?;
let bytes = message.into_bytes(); let bytes = message.into_bytes();