From 55df9bce090a82b1bb5f6cd14cf1009fed8468e1 Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Wed, 6 Jul 2022 23:30:50 +0200 Subject: [PATCH] main: Handle cosmic-session ipc --- Cargo.toml | 5 +- src/main.rs | 3 + src/session.rs | 160 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 src/session.rs diff --git a/Cargo.toml b/Cargo.toml index 04454392..b092df56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,8 @@ slog-async = "2.7" slog-scope = "4.4" slog-stdlog = "4.1" serde = { version = "1", features = ["derive"] } -serde_json = { version = "1", optional = true } +serde_json = "1" +sendfd = "0.4.1" egui = { version = "0.18.1", optional = true } edid-rs = { version = "0.1" } lazy_static = "1.4.0" @@ -45,7 +46,7 @@ optional = true [features] default = [] -debug = ["egui", "smithay-egui", "serde_json"] +debug = ["egui", "smithay-egui"] experimental = [] [profile.dev] diff --git a/src/main.rs b/src/main.rs index 5063c0ae..89e734e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ pub mod backend; pub mod config; pub mod input; mod logger; +pub mod session; pub mod shell; pub mod state; pub mod systemd; @@ -47,6 +48,8 @@ fn main() -> Result<()> { ); // init backend backend::init_backend_auto(&display.handle(), &mut event_loop, &mut state)?; + // potentially tell the session we are setup now + session::setup_socket(event_loop.handle(), &state)?; // potentially tell systemd we are setup now systemd::ready(&state); diff --git a/src/session.rs b/src/session.rs new file mode 100644 index 00000000..7a2eb3e7 --- /dev/null +++ b/src/session.rs @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use smithay::reexports::{ + calloop::{generic::Generic, Interest, LoopHandle, Mode, PostAction}, + nix::{fcntl, unistd}, +}; + +use anyhow::{anyhow, Context, Result}; +use sendfd::RecvWithFd; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + io::{Read, Write}, + os::unix::{ + io::{AsRawFd, FromRawFd, RawFd}, + net::UnixStream, + }, + sync::Arc, +}; + +use crate::state::{Data, State}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case", tag = "message")] +pub enum Message { + SetEnv { variables: HashMap }, + NewPrivilegedClient { count: usize }, +} + +struct StreamWrapper { + stream: UnixStream, + buffer: Vec, + size: u16, + read_bytes: usize, +} +impl AsRawFd for StreamWrapper { + fn as_raw_fd(&self) -> RawFd { + self.stream.as_raw_fd() + } +} +impl From for StreamWrapper { + fn from(stream: UnixStream) -> StreamWrapper { + StreamWrapper { + stream, + buffer: Vec::new(), + size: 0, + read_bytes: 0, + } + } +} + +pub fn setup_socket(handle: LoopHandle, state: &State) -> Result<()> { + if let Ok(fd_num) = std::env::var("COSMIC_SESSION_SOCK") { + if let Ok(fd) = fd_num.parse::() { + // set CLOEXEC + let flags = fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFD); + let result = flags + .map(|f| fcntl::FdFlag::from_bits(f).unwrap() | fcntl::FdFlag::FD_CLOEXEC) + .and_then(|f| fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFD(f))); + let mut session_socket = match result { + // 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) => { + let _ = unistd::close(fd); + return Err(err).with_context(|| "Failed to setup session socket"); + } + }; + + let mut env = HashMap::new(); + env.insert( + String::from("WAYLAND_DISPLAY"), + state + .common + .socket + .clone() + .into_string() + .map_err(|_| anyhow!("wayland socket is no valid utf-8 string?"))?, + ); + let message = serde_json::to_string(&Message::SetEnv { variables: env }) + .with_context(|| "Failed to encode environment variables into json")?; + let bytes = message.into_bytes(); + let len = (bytes.len() as u16).to_ne_bytes(); + session_socket + .write_all(&len) + .with_context(|| "Failed to write message len")?; + session_socket + .write_all(&bytes) + .with_context(|| "Failed to write message bytes")?; + + handle.insert_source( + Generic::new(StreamWrapper::from(session_socket), Interest::READ, Mode::Level), + move |_, stream, data: &mut crate::state::Data| { + if stream.size == 0 { + let mut len = [0u8; 2]; + match stream.stream.read_exact(&mut len) { + Ok(()) => { + stream.size = u16::from_ne_bytes(len); + stream.buffer = vec![0; stream.size as usize]; + }, + Err(err) => { + slog_scope::warn!("Error reading from session socket: {}", err); + return Ok(PostAction::Remove); + } + } + } + + stream.read_bytes += match stream.stream.read(&mut stream.buffer) { + Ok(size) => size, + Err(err) => { + slog_scope::error!("Error reading from session socket: {}", err); + return Ok(PostAction::Remove); + } + }; + + if stream.read_bytes != 0 && stream.read_bytes == stream.size as usize { + stream.size = 0; + stream.read_bytes = 0; + match std::str::from_utf8(&stream.buffer) { + Ok(message) => { + match serde_json::from_str::<'_, Message>(&message) { + Ok(Message::NewPrivilegedClient { count }) => { + let mut buffer = [0; 1]; + let mut fds = vec![0; count]; + match stream.stream.recv_with_fd(&mut buffer, &mut *fds) { + Ok((_, received_count)) => { + assert_eq!(received_count, count); + for fd in fds.into_iter().take(received_count) { + let stream = unsafe { UnixStream::from_raw_fd(fd) }; + if let Err(err) = data.display.handle().insert_client(stream, Arc::new(data.state.new_privileged_client_state())) { + slog_scope::warn!("Failed to add privileged client to display: {}", err); + } + } + }, + Err(err) => { + slog_scope::warn!("Failed to read file descriptors from session sock: {}", err); + } + } + }, + Ok(Message::SetEnv { .. }) => slog_scope::warn!("Got SetEnv from session? What is this?"), + _ => slog_scope::warn!("Unknown session socket message, are you using incompatible cosmic-session and cosmic-comp versions?"), + }; + Ok(PostAction::Continue) + }, + Err(err) => { + slog_scope::warn!("Invalid message from session sock: {}", err); + Ok(PostAction::Continue) + } + } + } else { + Ok(PostAction::Continue) + } + }, + ).with_context(|| "Failed to init the cosmic session socket source")?; + } + slog_scope::error!("COSMIC_SESSION_SOCK is no valid RawFd: {}", fd_num); + }; + + Ok(()) +}