Skip to content

Commit

Permalink
Merge pull request #180 from mahkoh/jorth/client-caps
Browse files Browse the repository at this point in the history
wayland: implement wp-security-manager-v1
  • Loading branch information
mahkoh authored Apr 24, 2024
2 parents e543646 + 1fceffe commit 4a236b7
Show file tree
Hide file tree
Showing 29 changed files with 533 additions and 103 deletions.
6 changes: 3 additions & 3 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ keymap = """
"""

# An action that will be executed when the GPU has been initialized.
on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } }
on-graphics-initialized = { type = "exec", exec = "mako" }

# Shortcuts that are processed by the compositor.
# The left hand side should be a key, possibly prefixed with modifiers.
Expand Down Expand Up @@ -79,7 +79,7 @@ alt-shift-f = "toggle-floating"
# For example, the exec action spawns an application and has the exec field
# that describes how to spawn the application.
Super_L = { type = "exec", exec = "alacritty" }
alt-p = { type = "exec", exec = { prog = "bemenu-run", privileged = true } }
alt-p = { type = "exec", exec = "bemenu-run" }

# The quit action terminates the compositor.
alt-q = "quit"
Expand Down Expand Up @@ -266,7 +266,7 @@ If you want to run an action at startup, you can use the top-level `on-graphics-
field:

```toml
on-graphics-initialized = { type = "exec", exec = { prog = "mako", privileged = true } }
on-graphics-initialized = { type = "exec", exec = "mako" }
```

### Setting Environment Variables
Expand Down
78 changes: 40 additions & 38 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,48 +122,50 @@ Jay's shortcut system allows you to execute an action when a key is pressed and

Jay supports the following wayland protocols:

| Global | Version | Privileged |
|-----------------------------------------|:-----------------|------------|
| ext_foreign_toplevel_list_v1 | 1 | Yes |
| ext_idle_notifier_v1 | 1 | Yes |
| ext_session_lock_manager_v1 | 1 | Yes |
| org_kde_kwin_server_decoration_manager | 1 | |
| wl_compositor | 6[^no_touch] | |
| wl_data_device_manager | 3 | |
| wl_drm | 2 | |
| wl_output | 4 | |
| wl_seat | 9 | |
| wl_shm | 2 | |
| wl_subcompositor | 1 | |
| wp_alpha_modifier_v1 | 1 | |
| wp_content_type_manager_v1 | 1 | |
| wp_cursor_shape_manager_v1 | 1 | |
| wp_fractional_scale_manager_v1 | 1 | |
| wp_linux_drm_syncobj_manager_v1 | 1 | |
| wp_presentation | 1 | |
| wp_single_pixel_buffer_manager_v1 | 1 | |
| wp_tearing_control_manager_v1 | 1[^no_tearing] | |
| wp_viewporter | 1 | |
| xdg_activation_v1 | 1 | |
| xdg_toplevel_drag_manager_v1 | 1 | |
| xdg_wm_base | 6 | |
| zwlr_data_control_manager_v1 | 2 | Yes |
| zwlr_layer_shell_v1 | 4[^no_exclusive] | Yes |
| zwlr_screencopy_manager_v1 | 3 | Yes |
| zwp_idle_inhibit_manager_v1 | 1 | |
| zwp_input_method_manager_v2 | 1 | Yes |
| zwp_linux_dmabuf_v1 | 5 | |
| zwp_pointer_constraints_v1 | 1 | |
| zwp_primary_selection_device_manager_v1 | 1 | |
| zwp_relative_pointer_manager_v1 | 1 | |
| zwp_text_input_manager_v3 | 1 | |
| zwp_virtual_keyboard_manager_v1 | 1 | Yes |
| zxdg_decoration_manager_v1 | 1 | |
| zxdg_output_manager_v1 | 3 | |
| Global | Version | Privileged |
|-----------------------------------------|:-----------------|---------------|
| ext_foreign_toplevel_list_v1 | 1 | Yes |
| ext_idle_notifier_v1 | 1 | Yes |
| ext_session_lock_manager_v1 | 1 | Yes |
| org_kde_kwin_server_decoration_manager | 1 | |
| wl_compositor | 6[^no_touch] | |
| wl_data_device_manager | 3 | |
| wl_drm | 2 | |
| wl_output | 4 | |
| wl_seat | 9 | |
| wl_shm | 2 | |
| wl_subcompositor | 1 | |
| wp_alpha_modifier_v1 | 1 | |
| wp_content_type_manager_v1 | 1 | |
| wp_cursor_shape_manager_v1 | 1 | |
| wp_fractional_scale_manager_v1 | 1 | |
| wp_linux_drm_syncobj_manager_v1 | 1 | |
| wp_presentation | 1 | |
| wp_security_context_manager_v1 | 1 | |
| wp_single_pixel_buffer_manager_v1 | 1 | |
| wp_tearing_control_manager_v1 | 1[^no_tearing] | |
| wp_viewporter | 1 | |
| xdg_activation_v1 | 1 | |
| xdg_toplevel_drag_manager_v1 | 1 | |
| xdg_wm_base | 6 | |
| zwlr_data_control_manager_v1 | 2 | Yes |
| zwlr_layer_shell_v1 | 4[^no_exclusive] | No[^lsaccess] |
| zwlr_screencopy_manager_v1 | 3 | Yes |
| zwp_idle_inhibit_manager_v1 | 1 | |
| zwp_input_method_manager_v2 | 1 | Yes |
| zwp_linux_dmabuf_v1 | 5 | |
| zwp_pointer_constraints_v1 | 1 | |
| zwp_primary_selection_device_manager_v1 | 1 | |
| zwp_relative_pointer_manager_v1 | 1 | |
| zwp_text_input_manager_v3 | 1 | |
| zwp_virtual_keyboard_manager_v1 | 1 | Yes |
| zxdg_decoration_manager_v1 | 1 | |
| zxdg_output_manager_v1 | 3 | |

[^no_touch]: Touch input is not supported.
[^no_tearing]: Tearing screen updates are not supported.
[^no_exclusive]: Exclusive zones are not supported.
[^lsaccess]: Sandboxes can restrict access to this protocol.

## Missing Features

Expand Down
2 changes: 2 additions & 0 deletions release-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

- Add support for wp-security-manager-v1.

# 1.1.0 (2024-04-22)

- Screencasts now support window capture.
Expand Down
24 changes: 16 additions & 8 deletions src/acceptor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use {
crate::{
async_engine::SpawnedFuture,
client::{ClientCaps, CAP_LAYER_SHELL},
state::State,
utils::{errorfmt::ErrorFmt, oserror::OsError, xrd::xrd},
},
Expand Down Expand Up @@ -145,12 +146,16 @@ impl Acceptor {
}
let acc = Rc::new(Acceptor { socket });
let futures = vec![
state
.eng
.spawn(accept(acc.socket.secure.clone(), state.clone(), true)),
state
.eng
.spawn(accept(acc.socket.insecure.clone(), state.clone(), false)),
state.eng.spawn(accept(
acc.socket.secure.clone(),
state.clone(),
ClientCaps::all(),
)),
state.eng.spawn(accept(
acc.socket.insecure.clone(),
state.clone(),
CAP_LAYER_SHELL,
)),
];
state.acceptor.set(Some(acc.clone()));
Ok((acc, futures))
Expand All @@ -166,7 +171,7 @@ impl Acceptor {
}
}

async fn accept(fd: Rc<OwnedFd>, state: Rc<State>, secure: bool) {
async fn accept(fd: Rc<OwnedFd>, state: Rc<State>, effective_caps: ClientCaps) {
loop {
let fd = match state.ring.accept(&fd, c::SOCK_CLOEXEC).await {
Ok(fd) => fd,
Expand All @@ -176,7 +181,10 @@ async fn accept(fd: Rc<OwnedFd>, state: Rc<State>, secure: bool) {
}
};
let id = state.clients.id();
if let Err(e) = state.clients.spawn(id, &state, fd, secure) {
if let Err(e) = state
.clients
.spawn(id, &state, fd, effective_caps, ClientCaps::all())
{
log::error!("Could not spawn a client: {}", ErrorFmt(e));
break;
}
Expand Down
45 changes: 36 additions & 9 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ mod error;
mod objects;
mod tasks;

bitflags! {
ClientCaps: u32;
CAP_DATA_CONTROL_MANAGER = 1 << 0,
CAP_VIRTUAL_KEYBOARD_MANAGER = 1 << 1,
CAP_FOREIGN_TOPLEVEL_LIST = 1 << 2,
CAP_IDLE_NOTIFIER = 1 << 3,
CAP_SESSION_LOCK_MANAGER = 1 << 4,
CAP_JAY_COMPOSITOR = 1 << 5,
CAP_LAYER_SHELL = 1 << 6,
CAP_SCREENCOPY_MANAGER = 1 << 7,
}

#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct ClientId(u64);

Expand Down Expand Up @@ -101,7 +113,8 @@ impl Clients {
id: ClientId,
global: &Rc<State>,
socket: Rc<OwnedFd>,
secure: bool,
effective_caps: ClientCaps,
bounding_caps: ClientCaps,
) -> Result<(), ClientError> {
let (uid, pid) = {
let mut cred = c::ucred {
Expand All @@ -120,7 +133,16 @@ impl Clients {
}
}
};
self.spawn2(id, global, socket, uid, pid, secure, false)?;
self.spawn2(
id,
global,
socket,
uid,
pid,
effective_caps,
bounding_caps,
false,
)?;
Ok(())
}

Expand All @@ -131,7 +153,8 @@ impl Clients {
socket: Rc<OwnedFd>,
uid: c::uid_t,
pid: c::pid_t,
secure: bool,
effective_caps: ClientCaps,
bounding_caps: ClientCaps,
is_xwayland: bool,
) -> Result<Rc<Client>, ClientError> {
let data = Rc::new(Client {
Expand All @@ -145,7 +168,8 @@ impl Clients {
shutdown: Default::default(),
tracker: Default::default(),
is_xwayland,
secure,
effective_caps,
bounding_caps,
last_enter_serial: Cell::new(0),
pid_info: get_pid_info(uid, pid),
serials: Default::default(),
Expand All @@ -165,13 +189,13 @@ impl Clients {
data: data.clone(),
};
log::info!(
"Client {} connected, pid: {}, uid: {}, fd: {}, secure: {}, comm: {:?}",
"Client {} connected, pid: {}, uid: {}, fd: {}, comm: {:?}, caps: {:?}",
id,
pid,
uid,
client.data.socket.raw(),
secure,
data.pid_info.comm,
effective_caps,
);
self.clients.borrow_mut().insert(client.data.id, client);
Ok(data)
Expand All @@ -193,13 +217,15 @@ impl Clients {
}
}

pub fn broadcast<B>(&self, secure: bool, xwayland_only: bool, mut f: B)
pub fn broadcast<B>(&self, required_caps: ClientCaps, xwayland_only: bool, mut f: B)
where
B: FnMut(&Rc<Client>),
{
let clients = self.clients.borrow();
for client in clients.values() {
if (!secure || client.data.secure) && (!xwayland_only || client.data.is_xwayland) {
if client.data.effective_caps.contains(required_caps)
&& (!xwayland_only || client.data.is_xwayland)
{
f(&client.data);
}
}
Expand Down Expand Up @@ -258,7 +284,8 @@ pub struct Client {
shutdown: AsyncEvent,
pub tracker: Tracker<Client>,
pub is_xwayland: bool,
pub secure: bool,
pub effective_caps: ClientCaps,
pub bounding_caps: ClientCaps,
pub last_enter_serial: Cell<u32>,
pub pid_info: PidInfo,
pub serials: RefCell<VecDeque<SerialRange>>,
Expand Down
1 change: 1 addition & 0 deletions src/compositor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ fn start_compositor2(
wait_for_sync_obj: Rc::new(WaitForSyncObj::new(&ring, &engine)),
explicit_sync_enabled: Cell::new(true),
keyboard_state_ids: Default::default(),
security_context_acceptors: Default::default(),
});
state.tracker.register(ClientId::from_raw(0));
create_dummy_output(&state);
Expand Down
28 changes: 17 additions & 11 deletions src/globals.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use {
crate::{
backend::Backend,
client::Client,
client::{Client, ClientCaps},
ifs::{
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1Global,
ext_idle_notifier_v1::ExtIdleNotifierV1Global,
Expand Down Expand Up @@ -34,6 +34,7 @@ use {
wp_cursor_shape_manager_v1::WpCursorShapeManagerV1Global,
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1Global,
wp_presentation::WpPresentationGlobal,
wp_security_context_manager_v1::WpSecurityContextManagerV1Global,
wp_single_pixel_buffer_manager_v1::WpSinglePixelBufferManagerV1Global,
wp_tearing_control_manager_v1::WpTearingControlManagerV1Global,
wp_viewporter::WpViewporterGlobal,
Expand Down Expand Up @@ -113,8 +114,8 @@ pub trait Global: GlobalBase {
fn singleton(&self) -> bool;
fn version(&self) -> u32;
fn break_loops(&self) {}
fn secure(&self) -> bool {
false
fn required_caps(&self) -> ClientCaps {
ClientCaps::none()
}
fn xwayland_only(&self) -> bool {
false
Expand Down Expand Up @@ -184,6 +185,7 @@ impl Globals {
add_singleton!(ZwpVirtualKeyboardManagerV1Global);
add_singleton!(ZwpInputMethodManagerV2Global);
add_singleton!(ZwpTextInputManagerV3Global);
add_singleton!(WpSecurityContextManagerV1Global);
}

pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
Expand Down Expand Up @@ -215,19 +217,21 @@ impl Globals {

fn insert(&self, state: &State, global: Rc<dyn Global>) {
self.insert_no_broadcast_(&global);
self.broadcast(state, global.secure(), global.xwayland_only(), |r| {
self.broadcast(state, global.required_caps(), global.xwayland_only(), |r| {
r.send_global(&global)
});
}

pub fn get(
&self,
name: GlobalName,
allow_secure: bool,
client_caps: ClientCaps,
allow_xwayland_only: bool,
) -> Result<Rc<dyn Global>, GlobalsError> {
let global = self.take(name, false)?;
if (global.secure() && !allow_secure) || (global.xwayland_only() && !allow_xwayland_only) {
if client_caps.not_contains(global.required_caps())
|| (global.xwayland_only() && !allow_xwayland_only)
{
return Err(GlobalsError::GlobalDoesNotExist(name));
}
Ok(global)
Expand All @@ -236,7 +240,7 @@ impl Globals {
pub fn remove<T: WaylandGlobal>(&self, state: &State, global: &T) -> Result<(), GlobalsError> {
let _global = self.take(global.name(), true)?;
global.remove(self);
self.broadcast(state, global.secure(), global.xwayland_only(), |r| {
self.broadcast(state, global.required_caps(), global.xwayland_only(), |r| {
r.send_global_remove(global.name())
});
Ok(())
Expand All @@ -247,14 +251,16 @@ impl Globals {
}

pub fn notify_all(&self, registry: &Rc<WlRegistry>) {
let secure = registry.client.secure;
let caps = registry.client.effective_caps;
let xwayland = registry.client.is_xwayland;
let globals = self.registry.lock();
macro_rules! emit {
($singleton:expr) => {
for global in globals.values() {
if global.singleton() == $singleton {
if (secure || !global.secure()) && (xwayland || !global.xwayland_only()) {
if caps.contains(global.required_caps())
&& (xwayland || !global.xwayland_only())
{
registry.send_global(global);
}
}
Expand All @@ -268,11 +274,11 @@ impl Globals {
fn broadcast<F: Fn(&Rc<WlRegistry>)>(
&self,
state: &State,
secure: bool,
required_caps: ClientCaps,
xwayland_only: bool,
f: F,
) {
state.clients.broadcast(secure, xwayland_only, |c| {
state.clients.broadcast(required_caps, xwayland_only, |c| {
let registries = c.lock_registries();
for registry in registries.values() {
f(registry);
Expand Down
Loading

0 comments on commit 4a236b7

Please sign in to comment.