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