main: Handle cosmic-session ipc

This commit is contained in:
Victoria Brekenfeld 2022-07-06 23:30:50 +02:00
parent e480ed5d1c
commit 55df9bce09
3 changed files with 166 additions and 2 deletions

View file

@ -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]

View file

@ -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);

160
src/session.rs Normal file
View file

@ -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<String, String> },
NewPrivilegedClient { count: usize },
}
struct StreamWrapper {
stream: UnixStream,
buffer: Vec<u8>,
size: u16,
read_bytes: usize,
}
impl AsRawFd for StreamWrapper {
fn as_raw_fd(&self) -> RawFd {
self.stream.as_raw_fd()
}
}
impl From<UnixStream> for StreamWrapper {
fn from(stream: UnixStream) -> StreamWrapper {
StreamWrapper {
stream,
buffer: Vec::new(),
size: 0,
read_bytes: 0,
}
}
}
pub fn setup_socket(handle: LoopHandle<Data>, state: &State) -> Result<()> {
if let Ok(fd_num) = std::env::var("COSMIC_SESSION_SOCK") {
if let Ok(fd) = fd_num.parse::<RawFd>() {
// 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(())
}