in_container/
lib.rs

1// Copyright Pit Kleyersburg <pitkley@googlemail.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified or distributed
7// except according to those terms.
8
9#![forbid(missing_docs, unsafe_code)]
10
11//! # in-container
12//!
13//! `in-container` is a binary and a library that can be used to detect if you are running inside a
14//! container. Executing the binary will by default return exit-code 0 if it was run inside a
15//! container and exit-code 1 if it wasn't. The library can be included in an application of your
16//! choice, allowing you to determine whether your application is running inside a container or not.
17//!
18//! (Please note that some of the detection mechanisms only work if `in-container` is executed in a
19//! privileged context.)
20//!
21//! ## Supported operating systems/containerization solutions
22//!
23//! * FreeBSD
24//!     * [Jails](https://www.freebsd.org/doc/handbook/jails.html)
25//! * Linux
26//!     * [Docker](https://docs.docker.com/engine/)
27//!     * [LXC](https://linuxcontainers.org/)
28//!     * [systemd-nspawn](https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html)
29//! * Windows
30//!     * [Docker](https://docs.docker.com/docker-for-windows/install/)
31//!
32//! If you are missing support for an operating system or container runtime, feel free to [open a
33//! feature request](https://github.com/pitkley/in-container/issues/new) or
34//! [open a pull request](https://github.com/pitkley/in-container/pull/compare).
35//!
36//! ## Usage as a library
37//!
38//! Add `in-container` as a dependency to your project's `Cargo.toml`:
39//!
40//! ```toml
41//! [dependencies]
42//! in-container = "^1"
43//! ```
44//!
45//! You can then use `in_container::in_container()` which will return `true` if you are running
46//! inside a container and `false` otherwise. In case you are interested in the container-runtime
47//! that was detected, you can call `in_container::get_container_runtime()` instead, which will
48//! return an `Option<ContainerRuntime>`. The `Option` is `None` when not running in a container,
49//! otherwise it will contain the detected runtime.
50//!
51//! ## <a name="license"></a> License
52//!
53//! This project is licensed under either of
54//!
55//! * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
56//! <https://www.apache.org/licenses/LICENSE-2.0>)
57//! * MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
58//!
59//! at your option.
60//!
61//! ### <a name="license-contribution"></a> Contribution
62//!
63//! Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
64//! this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above,
65//! without any additional terms or conditions.
66
67use std::{fmt::Display, str::FromStr};
68
69/// Returns `true` if called from inside a container, `false` otherwise.
70pub fn in_container() -> bool {
71    get_container_runtime().is_some()
72}
73
74/// Optionally returns the detected [`ContainerRuntime`] if called from inside a container.
75///
76/// [`ContainerRuntime`]: enum.ContainerRuntime.html
77pub fn get_container_runtime() -> Option<ContainerRuntime> {
78    #[cfg(target_os = "freebsd")]
79    return freebsd::get_container_runtime();
80    #[cfg(target_os = "linux")]
81    return linux::get_container_runtime();
82    #[cfg(target_os = "windows")]
83    return windows::get_container_runtime();
84    #[cfg(not(any(target_os = "freebsd", target_os = "linux", target_os = "windows")))]
85    return None;
86}
87
88/// The detected container runtime.
89#[derive(Debug, Clone, Eq, PartialEq)]
90#[non_exhaustive]
91pub enum ContainerRuntime {
92    /// Docker container runtime
93    Docker,
94    /// BSD jail
95    Jail,
96    /// Linux Containers
97    Lxc,
98    /// systemd-nspawn
99    SystemdNspawn,
100    /// The detected container runtime is unknown
101    Unknown(String),
102}
103
104impl Display for ContainerRuntime {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        match self {
107            ContainerRuntime::Docker => write!(f, "docker"),
108            ContainerRuntime::Jail => write!(f, "jail"),
109            ContainerRuntime::Lxc => write!(f, "lxc"),
110            ContainerRuntime::SystemdNspawn => write!(f, "systemd-nspawn"),
111            ContainerRuntime::Unknown(name) => write!(f, "unknown({})", name),
112        }
113    }
114}
115
116impl From<&str> for ContainerRuntime {
117    fn from(s: &str) -> Self {
118        match s {
119            "docker" => Self::Docker,
120            "jail" => Self::Jail,
121            "lxc" => Self::Lxc,
122            "systemd-nspawn" => Self::SystemdNspawn,
123            name => Self::Unknown(name.to_owned()),
124        }
125    }
126}
127
128impl FromStr for ContainerRuntime {
129    type Err = ();
130    fn from_str(s: &str) -> Result<Self, Self::Err> {
131        Ok(s.into())
132    }
133}
134
135macro_rules! chain {
136    ( $fn:path $(,)+ $( $tail:path $(,)* )* ) => {
137        $fn().or_else(|| chain!( $($tail , )* ))
138    };
139    ( $fn:path ) => {
140        $fn()
141    };
142    () => { None};
143}
144
145#[cfg(target_os = "freebsd")]
146mod freebsd;
147#[cfg(target_os = "linux")]
148mod linux;
149#[cfg(target_os = "windows")]
150mod windows;
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn containerruntime_from_str() {
158        assert_eq!(ContainerRuntime::Docker, "docker".into());
159        assert_eq!(ContainerRuntime::Jail, "jail".into());
160        assert_eq!(ContainerRuntime::Lxc, "lxc".into());
161        assert_eq!(ContainerRuntime::SystemdNspawn, "systemd-nspawn".into());
162        assert_eq!(
163            ContainerRuntime::Unknown("garbage".to_owned()),
164            "garbage".into()
165        );
166        assert_eq!(ContainerRuntime::Unknown("".to_owned()), "".into());
167    }
168}