gardesk/garwarp / a8c0e5f

Browse files

export portal dbus interfaces

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
a8c0e5f5d582b300227516b44d88af3733c6935e
Parents
4b4a0fb
Tree
e9315a0

3 changed files

StatusFile+-
M README.md 16 11
M garwarp/src/dbus.rs 61 1
A scripts/test-dbus-interfaces.sh 45 0
README.mdmodified
@@ -9,24 +9,29 @@ Primary scope for the first implementation track:
9
 
9
 
10
 Planning documents live in `docs/` and are currently local-only.
10
 Planning documents live in `docs/` and are currently local-only.
11
 
11
 
12
-## Sprint 01 Status
12
+## Current Status
13
-Current scaffold includes:
13
+1. Sprint 01 complete: workspace, daemon lifecycle, control protocol scaffold, activation files.
14
-1. Rust workspace with `garwarp`, `garwarp-ipc`, and `garwarpctl`.
14
+2. Sprint 02 complete: control-plane hardening (idempotence, parser strictness, trusted peers, store durability/fallback, health recovery policy).
15
-2. Single-instance daemon lock with stale lock cleanup.
15
+3. Sprint 03 in progress: portal DBus interfaces are exported and introspectable at `/org/freedesktop/portal/desktop`.
16
-3. Local Unix socket control path (`status`, `stop`).
17
-4. D-Bus and systemd user activation file scaffolding.
18
 
16
 
19
 ## Local Commands
17
 ## Local Commands
20
 1. Start daemon: `cargo run -p garwarp -- daemon`
18
 1. Start daemon: `cargo run -p garwarp -- daemon`
21
 2. Check health and request counters: `cargo run -p garwarpctl -- status`
19
 2. Check health and request counters: `cargo run -p garwarpctl -- status`
22
 3. Stop daemon: `cargo run -p garwarpctl -- stop`
20
 3. Stop daemon: `cargo run -p garwarpctl -- stop`
23
 4. Verify D-Bus activation: `./scripts/test-dbus-activation.sh`
21
 4. Verify D-Bus activation: `./scripts/test-dbus-activation.sh`
24
-5. Verify request-store fallback: `./scripts/test-request-store-fallback.sh`
22
+5. Verify DBus interfaces are exported: `./scripts/test-dbus-interfaces.sh`
25
-6. Create mock request: `cargo run -p garwarpctl -- begin req-1 :1.2 - x11:0x2a`
23
+6. Verify request-store fallback: `./scripts/test-request-store-fallback.sh`
26
-7. Transition mock request: `cargo run -p garwarpctl -- transition req-1 :1.2 awaiting_user`
24
+7. Create mock request: `cargo run -p garwarpctl -- begin req-1 :1.2 - x11:0x2a`
27
-8. List known requests: `cargo run -p garwarpctl -- list`
25
+8. Transition mock request: `cargo run -p garwarpctl -- transition req-1 :1.2 awaiting_user`
28
-9. Inspect request snapshot: `cargo run -p garwarpctl -- inspect req-1`
26
+9. List known requests: `cargo run -p garwarpctl -- list`
27
+10. Inspect request snapshot: `cargo run -p garwarpctl -- inspect req-1`
29
 
28
 
30
 ## Runtime Tuning
29
 ## Runtime Tuning
31
 1. `GARWARP_REQUEST_TIMEOUT_MS`: timeout before in-flight requests are marked `expired`.
30
 1. `GARWARP_REQUEST_TIMEOUT_MS`: timeout before in-flight requests are marked `expired`.
32
 2. `GARWARP_TERMINAL_RETENTION_MS`: retention window before terminal requests are pruned.
31
 2. `GARWARP_TERMINAL_RETENTION_MS`: retention window before terminal requests are pruned.
32
+
33
+## Control Protocol Compatibility
34
+1. Protocol version is reported by `status protocol=<n>` and currently fixed at `v1`.
35
+2. Unknown or duplicate request fields are rejected as `invalid_request` (fail-closed parsing).
36
+3. Stable error reasons are part of the control contract; new reasons may be added, existing reasons should not be repurposed.
37
+4. Sender identity for begin/transition is derived from Unix peer credentials; payload `sender` is ignored for ownership.
garwarp/src/dbus.rsmodified
@@ -1,6 +1,8 @@
1
-use zbus::blocking::Connection;
1
+use zbus::{blocking::Connection, interface};
2
 
2
 
3
 pub const BACKEND_DBUS_NAME: &str = "org.freedesktop.impl.portal.desktop.garwarp";
3
 pub const BACKEND_DBUS_NAME: &str = "org.freedesktop.impl.portal.desktop.garwarp";
4
+pub const BACKEND_OBJECT_PATH: &str = "/org/freedesktop/portal/desktop";
5
+const INTERFACE_VERSION: u32 = 1;
4
 
6
 
5
 pub struct SessionNameGuard {
7
 pub struct SessionNameGuard {
6
     _connection: Connection,
8
     _connection: Connection,
@@ -10,8 +12,66 @@ impl SessionNameGuard {
10
     pub fn acquire() -> zbus::Result<Self> {
12
     pub fn acquire() -> zbus::Result<Self> {
11
         let connection = Connection::session()?;
13
         let connection = Connection::session()?;
12
         connection.request_name(BACKEND_DBUS_NAME)?;
14
         connection.request_name(BACKEND_DBUS_NAME)?;
15
+        {
16
+            let object_server = connection.object_server();
17
+            object_server.at(BACKEND_OBJECT_PATH, ScreenshotPortal)?;
18
+            object_server.at(BACKEND_OBJECT_PATH, OpenUriPortal)?;
19
+            object_server.at(BACKEND_OBJECT_PATH, AppChooserPortal)?;
20
+        }
13
         Ok(Self {
21
         Ok(Self {
14
             _connection: connection,
22
             _connection: connection,
15
         })
23
         })
16
     }
24
     }
17
 }
25
 }
26
+
27
+#[derive(Debug)]
28
+struct ScreenshotPortal;
29
+
30
+#[interface(name = "org.freedesktop.impl.portal.Screenshot")]
31
+impl ScreenshotPortal {
32
+    #[zbus(property)]
33
+    fn version(&self) -> u32 {
34
+        INTERFACE_VERSION
35
+    }
36
+}
37
+
38
+#[derive(Debug)]
39
+struct OpenUriPortal;
40
+
41
+#[interface(name = "org.freedesktop.impl.portal.OpenURI")]
42
+impl OpenUriPortal {
43
+    #[zbus(property)]
44
+    fn version(&self) -> u32 {
45
+        INTERFACE_VERSION
46
+    }
47
+}
48
+
49
+#[derive(Debug)]
50
+struct AppChooserPortal;
51
+
52
+#[interface(name = "org.freedesktop.impl.portal.AppChooser")]
53
+impl AppChooserPortal {
54
+    #[zbus(property)]
55
+    fn version(&self) -> u32 {
56
+        INTERFACE_VERSION
57
+    }
58
+}
59
+
60
+#[cfg(test)]
61
+mod tests {
62
+    use super::{
63
+        AppChooserPortal, BACKEND_OBJECT_PATH, INTERFACE_VERSION, OpenUriPortal, ScreenshotPortal,
64
+    };
65
+
66
+    #[test]
67
+    fn backend_object_path_is_portal_desktop_path() {
68
+        assert_eq!(BACKEND_OBJECT_PATH, "/org/freedesktop/portal/desktop");
69
+    }
70
+
71
+    #[test]
72
+    fn portal_interfaces_report_expected_version() {
73
+        assert_eq!(ScreenshotPortal.version(), INTERFACE_VERSION);
74
+        assert_eq!(OpenUriPortal.version(), INTERFACE_VERSION);
75
+        assert_eq!(AppChooserPortal.version(), INTERFACE_VERSION);
76
+    }
77
+}
scripts/test-dbus-interfaces.shadded
@@ -0,0 +1,45 @@
1
+#!/usr/bin/env bash
2
+set -euo pipefail
3
+
4
+repo_root="$(cd "$(dirname "$0")/.." && pwd)"
5
+runtime_dir="$(mktemp -d)"
6
+introspection_file="$(mktemp)"
7
+log_file="$(mktemp)"
8
+
9
+cleanup() {
10
+  rm -f "$introspection_file" "$log_file"
11
+  rm -rf "$runtime_dir"
12
+}
13
+trap cleanup EXIT
14
+
15
+DBUS_FATAL_WARNINGS=0 dbus-run-session -- bash -lc '
16
+set -euo pipefail
17
+export XDG_RUNTIME_DIR="'"$runtime_dir"'"
18
+cd "'"$repo_root"'"
19
+
20
+cargo run -q -p garwarp -- daemon >"'"$log_file"'" 2>&1 &
21
+pid=$!
22
+
23
+for _ in $(seq 1 200); do
24
+  if dbus-send --session --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus \
25
+      org.freedesktop.DBus.NameHasOwner string:org.freedesktop.impl.portal.desktop.garwarp \
26
+      | rg -q "boolean true"; then
27
+    break
28
+  fi
29
+  sleep 0.05
30
+done
31
+
32
+dbus-send --session --print-reply \
33
+  --dest=org.freedesktop.impl.portal.desktop.garwarp \
34
+  /org/freedesktop/portal/desktop \
35
+  org.freedesktop.DBus.Introspectable.Introspect >"'"$introspection_file"'"
36
+
37
+cargo run -q -p garwarpctl -- stop >/dev/null
38
+wait "$pid"
39
+'
40
+
41
+rg -q "org.freedesktop.impl.portal.Screenshot" "$introspection_file"
42
+rg -q "org.freedesktop.impl.portal.OpenURI" "$introspection_file"
43
+rg -q "org.freedesktop.impl.portal.AppChooser" "$introspection_file"
44
+
45
+printf "dbus interface export smoke test passed\n"