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}