ashpd/flatpak/mod.rs
1//! # Examples
2//!
3//! Spawn a process inside of the sandbox, only works in a Flatpak.
4//!
5//! ```rust,no_run
6//! use std::{collections::HashMap, os::fd::BorrowedFd};
7//!
8//! use ashpd::flatpak::{Flatpak, SpawnFlags, SpawnOptions};
9//!
10//! async fn run() -> ashpd::Result<()> {
11//! let proxy = Flatpak::new().await?;
12//! let fds: HashMap<_, BorrowedFd<'_>> = HashMap::new();
13//!
14//! proxy
15//! .spawn(
16//! "/",
17//! &["contrast"],
18//! fds,
19//! HashMap::new(),
20//! SpawnFlags::ClearEnv | SpawnFlags::NoNetwork,
21//! SpawnOptions::default(),
22//! )
23//! .await?;
24//!
25//! Ok(())
26//! }
27//! ```
28
29use std::{
30 collections::HashMap,
31 fmt::Debug,
32 os::fd::{AsFd, OwnedFd},
33 path::Path,
34};
35
36use enumflags2::{bitflags, BitFlags};
37use futures_util::Stream;
38use serde::Serialize;
39use serde_repr::{Deserialize_repr, Serialize_repr};
40use zbus::zvariant::{self, Fd, OwnedObjectPath, SerializeDict, Type};
41
42use crate::{proxy::Proxy, Error, FilePath, Pid};
43
44#[bitflags]
45#[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Copy, Clone, Debug, Type)]
46#[repr(u32)]
47/// A bitmask representing the "permissions" of a newly created sandbox.
48pub enum SandboxFlags {
49 /// Share the display access (X11, Wayland) with the caller.
50 DisplayAccess,
51 /// Share the sound access (PulseAudio) with the caller.
52 SoundAccess,
53 /// Share the gpu access with the caller.
54 GpuAccess,
55 /// Allow sandbox access to (filtered) session bus.
56 SessionBusAccess,
57 /// Allow sandbox access to accessibility bus.
58 AccessibilityBusAccess,
59}
60
61#[bitflags]
62#[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Copy, Clone, Debug, Type)]
63#[repr(u32)]
64#[doc(alias = "XdpSpawnFlags")]
65/// Flags affecting the created sandbox.
66pub enum SpawnFlags {
67 #[doc(alias = "XDP_SPAWN_FLAG_CLEARENV")]
68 /// Clear the environment.
69 ClearEnv,
70 #[doc(alias = "XDP_SPAWN_FLAG_LATEST")]
71 /// Spawn the latest version of the app.
72 LatestVersion,
73 #[doc(alias = "XDP_SPAWN_FLAG_SANDBOX")]
74 /// Spawn in a sandbox (equivalent of the sandbox option of `flatpak run`).
75 Sandbox,
76 #[doc(alias = "XDP_SPAWN_FLAG_NO_NETWORK")]
77 /// Spawn without network (equivalent of the `unshare=network` option of
78 /// `flatpak run`).
79 NoNetwork,
80 #[doc(alias = "XDP_SPAWN_FLAG_WATCH")]
81 /// Kill the sandbox when the caller disappears from the session bus.
82 WatchBus,
83 /// Expose the sandbox pids in the callers sandbox, only supported if using
84 /// user namespaces for containers (not setuid), see the support property.
85 ExposePids,
86 /// Emit a SpawnStarted signal once the sandboxed process has been fully
87 /// started.
88 NotifyStart,
89 /// Expose the sandbox process IDs in the caller's sandbox and the caller's
90 /// process IDs in the new sandbox.
91 SharePids,
92 /// Don't provide app files at `/app` in the new sandbox.
93 EmptyApp,
94}
95
96#[bitflags]
97#[derive(Serialize_repr, Deserialize_repr, PartialEq, Eq, Copy, Clone, Debug, Type)]
98#[repr(u32)]
99/// Flags marking what optional features are available.
100pub enum SupportsFlags {
101 /// Supports the expose sandbox pids flag of Spawn.
102 ExposePids,
103}
104
105#[derive(SerializeDict, Type, Debug, Default)]
106/// Specified options for a [`Flatpak::spawn`] request.
107#[zvariant(signature = "dict")]
108pub struct SpawnOptions {
109 /// A list of filenames for files inside the sandbox that will be exposed to
110 /// the new sandbox, for reading and writing.
111 #[zvariant(rename = "sandbox-expose")]
112 sandbox_expose: Option<Vec<String>>,
113 /// A list of filenames for files inside the sandbox that will be exposed to
114 /// the new sandbox, read-only.
115 #[zvariant(rename = "sandbox-expose-ro")]
116 sandbox_expose_ro: Option<Vec<String>>,
117 /// A list of file descriptor for files inside the sandbox that will be
118 /// exposed to the new sandbox, for reading and writing.
119 #[zvariant(rename = "sandbox-expose-fd")]
120 sandbox_expose_fd: Option<Vec<zvariant::OwnedFd>>,
121 /// A list of file descriptor for files inside the sandbox that will be
122 /// exposed to the new sandbox, read-only.
123 #[zvariant(rename = "sandbox-expose-fd-ro")]
124 sandbox_expose_fd_ro: Option<Vec<zvariant::OwnedFd>>,
125 /// Flags affecting the created sandbox.
126 #[zvariant(rename = "sandbox-flags")]
127 sandbox_flags: Option<BitFlags<SandboxFlags>>,
128 /// A list of environment variables to remove.
129 #[zvariant(rename = "unset-env")]
130 unset_env: Option<Vec<String>>,
131 /// A file descriptor of the directory that will be used as `/usr` in the
132 /// new sandbox.
133 #[zvariant(rename = "usr-fd")]
134 usr_fd: Option<zvariant::OwnedFd>,
135 /// A file descriptor of the directory that will be used as `/app` in the
136 /// new sandbox.
137 #[zvariant(rename = "app-fd")]
138 app_fd: Option<zvariant::OwnedFd>,
139}
140
141impl SpawnOptions {
142 /// Sets the list of filenames for files to expose the new sandbox.
143 /// **Note** absolute paths or subdirectories are not allowed.
144 #[must_use]
145 pub fn sandbox_expose<P: IntoIterator<Item = I>, I: AsRef<str> + Type + Serialize>(
146 mut self,
147 sandbox_expose: impl Into<Option<P>>,
148 ) -> Self {
149 self.sandbox_expose = sandbox_expose
150 .into()
151 .map(|a| a.into_iter().map(|s| s.as_ref().to_owned()).collect());
152 self
153 }
154
155 /// Sets the list of filenames for files to expose the new sandbox,
156 /// read-only.
157 /// **Note** absolute paths or subdirectories are not allowed.
158 #[must_use]
159 pub fn sandbox_expose_ro<P: IntoIterator<Item = I>, I: AsRef<str> + Type + Serialize>(
160 mut self,
161 sandbox_expose_ro: impl Into<Option<P>>,
162 ) -> Self {
163 self.sandbox_expose_ro = sandbox_expose_ro
164 .into()
165 .map(|a| a.into_iter().map(|s| s.as_ref().to_owned()).collect());
166 self
167 }
168
169 /// Sets the list of file descriptors of files to expose the new sandbox.
170 #[must_use]
171 pub fn sandbox_expose_fd<P: IntoIterator<Item = OwnedFd>>(
172 mut self,
173 sandbox_expose_fd: impl Into<Option<P>>,
174 ) -> Self {
175 self.sandbox_expose_fd = sandbox_expose_fd
176 .into()
177 .map(|a| a.into_iter().map(zvariant::OwnedFd::from).collect());
178 self
179 }
180
181 /// Sets the list of file descriptors of files to expose the new sandbox,
182 /// read-only.
183 #[must_use]
184 pub fn sandbox_expose_fd_ro<P: IntoIterator<Item = OwnedFd>>(
185 mut self,
186 sandbox_expose_fd_ro: impl Into<Option<P>>,
187 ) -> Self {
188 self.sandbox_expose_fd_ro = sandbox_expose_fd_ro
189 .into()
190 .map(|a| a.into_iter().map(zvariant::OwnedFd::from).collect());
191 self
192 }
193
194 /// Sets the created sandbox flags.
195 #[must_use]
196 pub fn sandbox_flags(
197 mut self,
198 sandbox_flags: impl Into<Option<BitFlags<SandboxFlags>>>,
199 ) -> Self {
200 self.sandbox_flags = sandbox_flags.into();
201 self
202 }
203
204 /// Env variables to unset.
205 #[must_use]
206 pub fn unset_env<P: IntoIterator<Item = I>, I: AsRef<str> + Type + Serialize>(
207 mut self,
208 env: impl Into<Option<P>>,
209 ) -> Self {
210 self.unset_env = env
211 .into()
212 .map(|a| a.into_iter().map(|s| s.as_ref().to_owned()).collect());
213 self
214 }
215
216 /// Set a file descriptor of the directory that will be used as `/usr` in
217 /// the new sandbox.
218 #[must_use]
219 pub fn usr_fd(mut self, fd: impl Into<Option<OwnedFd>>) -> Self {
220 self.usr_fd = fd.into().map(|f| f.into());
221 self
222 }
223
224 /// Set a file descriptor of the directory that will be used as `/app` in
225 /// the new sandbox.
226 #[must_use]
227 pub fn app_fd(mut self, fd: impl Into<Option<OwnedFd>>) -> Self {
228 self.app_fd = fd.into().map(|f| f.into());
229 self
230 }
231}
232
233#[derive(SerializeDict, Type, Debug, Default)]
234/// Specified options for a [`Flatpak::create_update_monitor`] request.
235///
236/// Currently there are no possible options yet.
237#[zvariant(signature = "dict")]
238struct CreateMonitorOptions {}
239
240/// The interface exposes some interactions with Flatpak on the host to the
241/// sandbox. For example, it allows you to restart the applications or start a
242/// more sandboxed instance.
243///
244/// Wrapper of the DBus interface: [`org.freedesktop.portal.Flatpak`](https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-org.freedesktop.portal.Flatpak).
245#[derive(Debug)]
246#[doc(alias = "org.freedesktop.portal.Flatpak")]
247pub struct Flatpak<'a>(Proxy<'a>);
248
249impl<'a> Flatpak<'a> {
250 /// Create a new instance of [`Flatpak`].
251 pub async fn new() -> Result<Flatpak<'a>, Error> {
252 let proxy = Proxy::new_flatpak("org.freedesktop.portal.Flatpak").await?;
253 Ok(Self(proxy))
254 }
255
256 /// Creates an update monitor object that will emit signals
257 /// when an update for the caller becomes available, and can be used to
258 /// install it.
259 ///
260 /// # Required version
261 ///
262 /// The method requires the 2nd version implementation of the portal and
263 /// would fail with [`Error::RequiresVersion`] otherwise.
264 ///
265 /// # Specifications
266 ///
267 /// See also [`CreateUpdateMonitor`](https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-method-org-freedesktop-portal-Flatpak.CreateUpdateMonitor).
268 #[doc(alias = "CreateUpdateMonitor")]
269 #[doc(alias = "xdp_portal_update_monitor_start")]
270 pub async fn create_update_monitor(&self) -> Result<UpdateMonitor<'a>, Error> {
271 let options = CreateMonitorOptions::default();
272 let path = self
273 .0
274 .call_versioned::<OwnedObjectPath>("CreateUpdateMonitor", &(options), 2)
275 .await?;
276
277 UpdateMonitor::new(path.into_inner()).await
278 }
279
280 /// Emitted when a process starts by [`spawn()`][`Flatpak::spawn`].
281 ///
282 /// # Specifications
283 ///
284 /// See also [`SpawnStarted`](https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-signal-org-freedesktop-portal-Flatpak.SpawnStarted).
285 #[doc(alias = "SpawnStarted")]
286 pub async fn receive_spawn_started(&self) -> Result<impl Stream<Item = (u32, u32)>, Error> {
287 self.0.signal("SpawnStarted").await
288 }
289
290 /// Emitted when a process started by [`spawn()`][`Flatpak::spawn`]
291 /// exits.
292 ///
293 /// # Specifications
294 ///
295 /// See also [`SpawnExited`](https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-signal-org-freedesktop-portal-Flatpak.SpawnExited).
296 #[doc(alias = "SpawnExited")]
297 #[doc(alias = "XdpPortal::spawn-exited")]
298 pub async fn receive_spawn_exited(&self) -> Result<impl Stream<Item = (u32, u32)>, Error> {
299 self.0.signal("SpawnExited").await
300 }
301
302 /// This methods let you start a new instance of your application,
303 /// optionally enabling a tighter sandbox.
304 ///
305 /// # Arguments
306 ///
307 /// * `cwd_path` - The working directory for the new process.
308 /// * `argv` - The argv for the new process, starting with the executable to
309 /// launch.
310 /// * `fds` - Array of file descriptors to pass to the new process.
311 /// * `envs` - Array of variable/value pairs for the environment of the new
312 /// process.
313 /// * `flags`
314 /// * `options` - A [`SpawnOptions`].
315 ///
316 /// # Returns
317 ///
318 /// The PID of the new process.
319 ///
320 /// # Specifications
321 ///
322 /// See also [`Spawn`](https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-method-org-freedesktop-portal-Flatpak.Spawn).
323 #[doc(alias = "Spawn")]
324 #[doc(alias = "xdp_portal_spawn")]
325 pub async fn spawn(
326 &self,
327 cwd_path: impl AsRef<Path>,
328 argv: &[impl AsRef<Path>],
329 fds: HashMap<u32, impl AsFd>,
330 envs: HashMap<&str, &str>,
331 flags: BitFlags<SpawnFlags>,
332 options: SpawnOptions,
333 ) -> Result<u32, Error> {
334 let cwd_path = FilePath::new(cwd_path)?;
335 let argv = argv
336 .iter()
337 .map(FilePath::new)
338 .collect::<Result<Vec<FilePath>, _>>()?;
339 let fds: HashMap<u32, Fd> = fds.iter().map(|(k, val)| (*k, Fd::from(val))).collect();
340 self.0
341 .call("Spawn", &(cwd_path, argv, fds, envs, flags, options))
342 .await
343 }
344
345 /// This methods let you send a Unix signal to a process that was started
346 /// [`spawn()`][`Flatpak::spawn`].
347 ///
348 /// # Arguments
349 ///
350 /// * `pid` - The PID of the process to send the signal to.
351 /// * `signal` - The signal to send.
352 /// * `to_process_group` - Whether to send the signal to the process group.
353 ///
354 /// # Specifications
355 ///
356 /// See also [`SpawnSignal`](https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-method-org-freedesktop-portal-Flatpak.SpawnSignal).
357 #[doc(alias = "SpawnSignal")]
358 #[doc(alias = "xdp_portal_spawn_signal")]
359 pub async fn spawn_signal(
360 &self,
361 pid: Pid,
362 signal: u32,
363 to_process_group: bool,
364 ) -> Result<(), Error> {
365 self.0
366 .call("SpawnSignal", &(pid, signal, to_process_group))
367 .await
368 }
369
370 /// Flags marking what optional features are available.
371 ///
372 /// # Specifications
373 ///
374 /// See also [`supports`](https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-property-org-freedesktop-portal-Flatpak.supports).
375 pub async fn supports(&self) -> Result<BitFlags<SupportsFlags>, Error> {
376 self.0
377 .property_versioned::<BitFlags<SupportsFlags>>("supports", 3)
378 .await
379 }
380}
381
382impl<'a> std::ops::Deref for Flatpak<'a> {
383 type Target = zbus::Proxy<'a>;
384
385 fn deref(&self) -> &Self::Target {
386 &self.0
387 }
388}
389
390/// Monitor if there's an update it and install it.
391mod update_monitor;
392pub use update_monitor::{UpdateInfo, UpdateMonitor, UpdateProgress, UpdateStatus};
393
394/// Provide for a way to execute processes outside of the sandbox
395mod development;
396pub use development::{Development, HostCommandFlags};