snapcraft/
lib.rs

1use std::collections::HashMap;
2use std::env;
3use std::path::{Path, PathBuf};
4
5/// Checks to see whether we are running within a snap, and if so return the real home directory for the current user.
6pub fn check_snap_home() -> (bool, Option<PathBuf>) {
7    if in_snap() {
8        (true, snap_real_home())
9    } else {
10        (false, None)
11    }
12}
13
14/// Checks whether we are running within a snap
15pub fn in_snap() -> bool {
16    snap().is_some()
17}
18
19/// Directory where the snap is mounted. This is where all the files in your snap are visible in the filesystem.
20/// All of the data in the snap is read-only and cannot be changed.
21///
22/// Typical value: `/snap/hello-world/27`
23pub fn snap() -> Option<String> {
24    env::var("SNAP").ok()
25}
26
27/// CPU architecture of the running system.
28/// Typical value `amd64`
29///
30/// Other values are: `i386`, `armhf`, `arm64`.
31pub fn snap_arch() -> Option<String> {
32    env::var("SNAP_ARCH").ok()
33}
34
35/// Directory for system data that is common across revisions of a snap.
36/// This directory is owned and writable by root and is meant to be used by background applications (daemons, services).
37/// Unlike `SNAP_DATA` this directory is not backed up and restored across snap refresh and revert operations.
38///
39/// Typical value: `/var/snap/hello-world/common`
40pub fn snap_common() -> Option<PathBuf> {
41    if let Ok(snap_common) = env::var("SNAP_COMMON") {
42        Some(Path::new(snap_common.as_str()).to_path_buf())
43    } else {
44        None
45    }
46}
47
48/// Directory for system data of a snap.
49/// This directory is owned and writable by root and is meant to be used by background applications (daemons, services).
50/// Unlike `SNAP_COMMON` this directory is backed up and restored across snap refresh and snap revert operations.
51///
52/// Typical value `/var/snap/hello-world/27`
53pub fn snap_data() -> Option<PathBuf> {
54    if let Ok(snap_data) = env::var("SNAP_DATA") {
55        Some(Path::new(snap_data.as_str()).to_path_buf())
56    } else {
57        None
58    }
59}
60
61/// The name of snap instance, including instance key if one is set (snapd 2.36+).
62/// For example snap `hello-world` with instance key `foo` has instance name equal to `hello-world_foo`.
63///
64/// Typical value: `hello-world`
65pub fn snap_instance_name() -> Option<String> {
66    env::var("SNAP_INSTANCE_NAME").ok()
67}
68
69/// Instance key if one was set during installation or empty (snapd 2.36+).
70/// For example instance `hello-world_foo` has an instance key `foo`.
71///
72/// Typical value: none
73pub fn snap_instance_key() -> Option<String> {
74    env::var("SNAP_INSTANCE_KEY").ok()
75}
76
77/// Directory with additional system libraries. This variable is used internally by snapcraft.
78/// The value is always `/var/lib/snapd/lib/gl:` Please note the colon at the end of that value,
79/// the variable is a colon-separated list.
80///
81/// The referenced directory is typically empty unless Nvidia proprietary drivers are in use.
82pub fn snap_library_path() -> Option<Vec<PathBuf>> {
83    if let Ok(snap_real_home) = env::var("SNAP_LIBRARY_PATH") {
84        let snap_lib_paths: Vec<PathBuf> = snap_real_home
85            .split(':')
86            .filter(|x| !x.is_empty())
87            .map(|x| Path::new(x).to_path_buf())
88            .collect();
89        Some(snap_lib_paths)
90    } else {
91        None
92    }
93}
94
95/// The name of the snap as specified in the `snapcraft.yaml` file.
96///
97/// Typical value: `hello-world`
98pub fn snap_name() -> Option<String> {
99    env::var("SNAP_NAME").ok()
100}
101
102/// The vanilla `HOME` environment variable before snapd-induced remapping, refer
103/// [Any way to acquire the originally set HOME environment variable? - snapcraft snapcraft.io](https://forum.snapcraft.io/t/any-way-to-acquire-the-originally-set-home-environment-variable/19475)
104/// for more info.
105///
106/// Available since `snapd 2.46`.
107pub fn snap_real_home() -> Option<PathBuf> {
108    if let Ok(snap_real_home) = env::var("SNAP_REAL_HOME") {
109        Some(Path::new(snap_real_home.as_str()).to_path_buf())
110    } else {
111        None
112    }
113}
114
115/// Revision of the snap, as allocated by the Snap Store on upload or as allocated by snapd for locally installed snaps.
116/// The Snap Store assigns monotonic revisions to each upload of a given snap.
117/// Snapd uses Snap Store revisions if accompanying assertions are available or uses a locally generated number.
118/// Locally generated numbers are prefixed with `x` to distinguish them from Snap Store uploads.
119///
120/// Typical value: `27` or `x1`
121pub fn snap_revision() -> Option<String> {
122    env::var("SNAP_REVISION").ok()
123}
124
125/// This variable is only exposed on [Ubuntu Core](https://snapcraft.io/docs/glossary#heading--ubuntu-core) systems, and
126/// was introduced with snapd 2.57.   It points to a snap-specific location on the ubuntu-save partition where the snap
127/// is allowed to store persistent files (like certificates or configuration files) that will survive a
128/// [factory reset](https://ubuntu.com/core/docs/recovery-modes#heading--factory) of the Ubuntu Core device.
129///
130/// See [ubuntu-save](https://ubuntu.com/core/docs/storage-layout#heading--save) in the Ubuntu Core documentation for
131/// more details on storage layout with this specific partition.
132pub fn snap_save_data() -> Option<PathBuf> {
133    if let Ok(snap_save_data) = env::var("SNAP_SAVE_DATA") {
134        Some(Path::new(snap_save_data.as_str()).to_path_buf())
135    } else {
136        None
137    }
138}
139
140/// Directory for user data that is common across revisions of a snap.   Unlike `SNAP_DATA`, data present in this
141/// directory is not backed up or restored across snap refresh and snap revert operations. The directory is suitable for
142/// large data that the application can access even if it was made or modified by a future version of a snap.
143///
144/// Typical value `/home/zyga/snap/hello-world/common`
145pub fn snap_user_common() -> Option<PathBuf> {
146    if let Ok(snap_user_common) = env::var("SNAP_USER_COMMON") {
147        Some(Path::new(snap_user_common.as_str()).to_path_buf())
148    } else {
149        None
150    }
151}
152
153/// Directory for user data.
154/// This directory is backed up and restored across `snap refresh` and `snap revert` operations.
155/// Typical value: `/home/zyga/snap/hello-world/27`
156///
157/// The final number there is `$SNAP_REVISION`.
158pub fn snap_user_data() -> Option<PathBuf> {
159    if let Ok(snap_user_data) = env::var("SNAP_USER_DATA") {
160        Some(Path::new(snap_user_data.as_str()).to_path_buf())
161    } else {
162        None
163    }
164}
165
166/// The version string as specified in the `snapcraft.yaml`
167///
168/// Typical value `6.3`
169pub fn snap_version() -> Option<String> {
170    env::var("SNAP_VERSION").ok()
171}
172
173/// A map of all of the environment variables that start with `SNAP_`
174pub fn snap_env() -> Option<HashMap<String, String>> {
175    let snap_map: HashMap<String, String> =
176        env::vars().filter(|(k, _)| k.starts_with("SNAP")).collect();
177    if snap_map.is_empty() {
178        None
179    } else {
180        Some(snap_map)
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use serial_test::serial;
187    use std::{env, path::Path};
188
189    fn setup() {
190        env::set_var("SNAP", "/snap/hello-world/27");
191        env::set_var("SNAP_ARCH", "armhf");
192        env::set_var("SNAP_COMMON", "/var/snap/hello-world/common");
193        env::set_var("SNAP_DATA", "/var/snap/hello-world/27");
194        env::set_var("SNAP_INSTANCE_NAME", "hello-world");
195        env::set_var("SNAP_INSTANCE_KEY", "foo");
196        env::set_var("SNAP_LIBRARY_PATH", "/var/lib/snapd/lib/gl:");
197        env::set_var("SNAP_NAME", "hello-world");
198        env::set_var("SNAP_REAL_HOME", "/home/user");
199        env::set_var("SNAP_REVISION", "27");
200        env::set_var("SNAP_SAVE_DATA", "/snap/hello-world/27/save");
201        env::set_var("SNAP_USER_COMMON", "/home/zyga/snap/hello-world/common");
202        env::set_var("SNAP_USER_DATA", "/home/zyga/snap/hello-world/27");
203        env::set_var("SNAP_VERSION", "v1.0.0");
204    }
205
206    fn unsetup() {
207        env::vars()
208            .filter(|(k, _)| k.starts_with("SNAP"))
209            .for_each(|(k, _)| env::remove_var(k));
210    }
211
212    #[test]
213    #[serial]
214    fn snap() {
215        setup();
216        let val = crate::snap();
217        assert!(val.is_some());
218        assert_eq!(val.unwrap(), "/snap/hello-world/27");
219        unsetup();
220    }
221
222    #[test]
223    #[serial]
224    fn in_snap() {
225        setup();
226        let val = crate::in_snap();
227        assert!(val);
228        unsetup();
229    }
230
231    #[test]
232    #[serial]
233    fn not_in_snap() {
234        let val = crate::in_snap();
235        assert!(!val);
236    }
237
238    #[test]
239    #[serial]
240    fn check_snap_home() {
241        setup();
242        let val = crate::check_snap_home();
243        assert!(val.0);
244        assert!(val.1.is_some());
245        assert_eq!(val.1.unwrap(), Path::new("/home/user").to_path_buf());
246        unsetup();
247    }
248
249    #[test]
250    #[serial]
251    fn not_check_snap_home() {
252        let val = crate::check_snap_home();
253        assert!(!val.0);
254        assert!(val.1.is_none());
255    }
256
257    #[test]
258    #[serial]
259    fn snap_arch() {
260        setup();
261        let val = crate::snap_arch();
262        assert!(val.is_some());
263        assert_eq!(val.unwrap(), "armhf");
264        unsetup();
265    }
266
267    #[test]
268    #[serial]
269    fn snap_data() {
270        setup();
271        let val = crate::snap_data();
272        assert!(val.is_some());
273        assert_eq!(
274            val.unwrap(),
275            Path::new("/var/snap/hello-world/27").to_path_buf()
276        );
277        unsetup();
278    }
279
280    #[test]
281    #[serial]
282    fn snap_instance_name() {
283        setup();
284        let val = crate::snap_instance_name();
285        assert!(val.is_some());
286        assert_eq!(val.unwrap(), "hello-world");
287        unsetup();
288    }
289
290    #[test]
291    #[serial]
292    fn snap_instance_key() {
293        setup();
294        let val = crate::snap_instance_key();
295        assert!(val.is_some());
296        assert_eq!(val.unwrap(), "foo");
297        unsetup();
298    }
299
300    #[test]
301    #[serial]
302    fn snap_library_path() {
303        setup();
304        let val = crate::snap_library_path();
305        assert!(val.is_some());
306        let val = val.unwrap();
307        assert_eq!(val.len(), 1);
308        assert_eq!(
309            val.first().unwrap().to_owned(),
310            Path::new("/var/lib/snapd/lib/gl").to_path_buf()
311        );
312        unsetup();
313    }
314
315    #[test]
316    #[serial]
317    fn snap_name() {
318        setup();
319        let val = crate::snap_name();
320        assert!(val.is_some());
321        assert_eq!(val.unwrap(), "hello-world");
322        unsetup();
323    }
324
325    #[test]
326    #[serial]
327    fn snap_real_home() {
328        setup();
329        let val = crate::snap_real_home();
330        assert!(val.is_some());
331        assert_eq!(val.unwrap(), Path::new("/home/user").to_path_buf());
332        unsetup();
333    }
334
335    #[test]
336    #[serial]
337    fn snap_revision() {
338        setup();
339        let val = crate::snap_revision();
340        assert!(val.is_some());
341        assert_eq!(val.unwrap(), "27");
342        unsetup();
343    }
344
345    #[test]
346    #[serial]
347    fn snap_save_data() {
348        setup();
349        let val = crate::snap_save_data();
350        assert!(val.is_some());
351        assert_eq!(
352            val.unwrap(),
353            Path::new("/snap/hello-world/27/save").to_path_buf()
354        );
355        unsetup();
356    }
357
358    #[test]
359    #[serial]
360    fn snap_user_common() {
361        setup();
362        let val = crate::snap_user_common();
363        assert!(val.is_some());
364        assert_eq!(
365            val.unwrap(),
366            Path::new("/home/zyga/snap/hello-world/common").to_path_buf()
367        );
368        unsetup();
369    }
370
371    #[test]
372    #[serial]
373    fn snap_user_data() {
374        setup();
375        let val = crate::snap_user_data();
376        assert!(val.is_some());
377        assert_eq!(
378            val.unwrap(),
379            Path::new("/home/zyga/snap/hello-world/27").to_path_buf()
380        );
381        unsetup();
382    }
383
384    #[test]
385    #[serial]
386    fn snap_version() {
387        setup();
388        let val = crate::snap_version();
389        assert!(val.is_some());
390        assert_eq!(val.unwrap(), "v1.0.0");
391        unsetup();
392    }
393
394    #[test]
395    #[serial]
396    fn snap_env() {
397        setup();
398        let val = crate::snap_env();
399        assert!(val.is_some());
400        if let Some(val) = val {
401            assert_eq!(val.len(), 14);
402            assert_eq!(val.get("SNAP").unwrap(), "/snap/hello-world/27");
403            assert_eq!(val.get("SNAP_ARCH").unwrap(), "armhf");
404            assert_eq!(
405                val.get("SNAP_COMMON").unwrap(),
406                "/var/snap/hello-world/common"
407            );
408            assert_eq!(val.get("SNAP_DATA").unwrap(), "/var/snap/hello-world/27");
409            assert_eq!(val.get("SNAP_INSTANCE_NAME").unwrap(), "hello-world");
410            assert_eq!(val.get("SNAP_INSTANCE_KEY").unwrap(), "foo");
411            assert_eq!(
412                val.get("SNAP_LIBRARY_PATH").unwrap(),
413                "/var/lib/snapd/lib/gl:"
414            );
415            assert_eq!(val.get("SNAP_NAME").unwrap(), "hello-world");
416            assert_eq!(val.get("SNAP_REAL_HOME").unwrap(), "/home/user");
417            assert_eq!(val.get("SNAP_REVISION").unwrap(), "27");
418            assert_eq!(
419                val.get("SNAP_SAVE_DATA").unwrap(),
420                "/snap/hello-world/27/save"
421            );
422            assert_eq!(
423                val.get("SNAP_USER_COMMON").unwrap(),
424                "/home/zyga/snap/hello-world/common"
425            );
426            assert_eq!(
427                val.get("SNAP_USER_DATA").unwrap(),
428                "/home/zyga/snap/hello-world/27"
429            );
430            assert_eq!(val.get("SNAP_VERSION").unwrap(), "v1.0.0");
431        }
432        unsetup();
433    }
434
435    #[test]
436    #[serial]
437    fn no_snap_env() {
438        unsetup();
439        let val = crate::snap_env();
440        assert!(val.is_none());
441    }
442}