cosmic-comp/src/session.rs

169 lines
7.1 KiB
Rust
Raw Normal View History

2022-07-06 23:30:50 +02:00
// 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::{
2023-09-29 21:33:16 +02:00
io::{AsFd, BorrowedFd, FromRawFd, RawFd},
2022-07-06 23:30:50 +02:00
net::UnixStream,
},
sync::Arc,
};
use tracing::{error, warn};
2022-07-06 23:30:50 +02:00
2023-09-29 21:33:16 +02:00
use crate::state::State;
2022-07-06 23:30:50 +02:00
#[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,
}
2023-09-29 21:33:16 +02:00
impl AsFd for StreamWrapper {
fn as_fd(&self) -> BorrowedFd<'_> {
self.stream.as_fd()
2022-07-06 23:30:50 +02:00
}
}
impl From<UnixStream> for StreamWrapper {
fn from(stream: UnixStream) -> StreamWrapper {
StreamWrapper {
stream,
buffer: Vec::new(),
size: 0,
read_bytes: 0,
}
}
}
2023-09-29 21:33:16 +02:00
pub fn setup_socket(handle: LoopHandle<State>, state: &State) -> Result<()> {
2022-07-06 23:30:50 +02:00
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?"))?,
);
2023-03-07 20:28:41 +01:00
if let Some(display) = state.common.xwayland_state.as_ref().map(|s| s.display) {
env.insert(String::from("DISPLAY"), format!(":{}", display));
}
2022-07-06 23:30:50 +02:00
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),
2023-09-29 21:33:16 +02:00
move |_, stream, state| {
// SAFETY: We don't drop the stream!
let stream = unsafe { stream.get_mut() };
2022-07-06 23:30:50 +02:00
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) => {
warn!(?err, "Error reading from session socket");
2022-07-06 23:30:50 +02:00
return Ok(PostAction::Remove);
}
}
}
stream.read_bytes += match stream.stream.read(&mut stream.buffer) {
Ok(size) => size,
Err(err) => {
error!(?err, "Error reading from session socket");
2022-07-06 23:30:50 +02:00
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) };
2023-09-29 21:33:16 +02:00
if let Err(err) = state.common.display_handle.insert_client(stream, Arc::new(state.new_privileged_client_state())) {
warn!(?err, "Failed to add privileged client to display");
2022-07-06 23:30:50 +02:00
}
}
},
Err(err) => {
warn!(?err, "Failed to read file descriptors from session sock");
2022-07-06 23:30:50 +02:00
}
}
},
Ok(Message::SetEnv { .. }) => warn!("Got SetEnv from session? What is this?"),
_ => warn!("Unknown session socket message, are you using incompatible cosmic-session and cosmic-comp versions?"),
2022-07-06 23:30:50 +02:00
};
Ok(PostAction::Continue)
},
Err(err) => {
warn!(?err, "Invalid message from session sock");
2022-07-06 23:30:50 +02:00
Ok(PostAction::Continue)
}
}
} else {
Ok(PostAction::Continue)
}
},
).with_context(|| "Failed to init the cosmic session socket source")?;
2022-07-11 22:51:41 +02:00
} else {
error!(socket = fd_num, "COSMIC_SESSION_SOCK is no valid RawFd.");
2022-07-06 23:30:50 +02:00
}
};
Ok(())
}