unshare/std_api.rs
1// This file was derived from rust's own libstd/process.rs with the following
2// copyright:
3//
4// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
5// file at the top-level directory of this distribution and at
6// http://rust-lang.org/COPYRIGHT.
7//
8use std::ffi::OsStr;
9use std::default::Default;
10use std::collections::HashMap;
11use std::collections::HashSet;
12use std::env;
13use std::path::Path;
14
15use libc::{uid_t, gid_t};
16use crate::ffi_util::ToCString;
17use crate::{Command, Stdio, Fd};
18
19
20impl Command {
21 /// Constructs a new `Command` for launching the program at
22 /// path `program`, with the following default configuration:
23 ///
24 /// * No arguments to the program
25 /// * Inherit the current process's environment
26 /// * Inherit the current process's working directory
27 /// * Inherit stdin/stdout/stderr for `spawn` or `status`, but create pipes for `output`
28 ///
29 /// Builder methods are provided to change these defaults and
30 /// otherwise configure the process.
31 pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
32 Command {
33 filename: program.to_cstring(),
34 args: vec![program.to_cstring()],
35 environ: None,
36 config: Default::default(),
37 chroot_dir: None,
38 pivot_root: None,
39 fds: vec![
40 (0, Fd::inherit()),
41 (1, Fd::inherit()),
42 (2, Fd::inherit()),
43 ].into_iter().collect(),
44 close_fds: Vec::new(),
45 id_map_commands: None,
46 pid_env_vars: HashSet::new(),
47 keep_caps: None,
48 before_unfreeze: None,
49 pre_exec: None,
50 }
51 }
52
53 /// Add an argument to pass to the program.
54 pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command {
55 self.args.push(arg.to_cstring());
56 self
57 }
58
59 /// Add multiple arguments to pass to the program.
60 pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Command {
61 self.args.extend(args.iter().map(ToCString::to_cstring));
62 self
63 }
64
65 // TODO(tailhook) It's only public for our run module any better way?
66 // TODO(tailhook) make it private
67 #[doc(hidden)]
68 pub fn init_env_map(&mut self) {
69 if self.environ.is_none() {
70 self.environ = Some(env::vars_os().collect());
71 }
72 }
73
74 /// Inserts or updates an environment variable mapping.
75 pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Command
76 where K: AsRef<OsStr>, V: AsRef<OsStr>
77 {
78 self.init_env_map();
79 self.environ.as_mut().unwrap().insert(
80 key.as_ref().to_os_string(),
81 val.as_ref().to_os_string());
82 self.pid_env_vars.remove(key.as_ref());
83 self
84 }
85
86 /// Inserts or updates multiple environment variable mappings.
87 pub fn envs<I, K, V>(&mut self, vars: I)-> &mut Command
88 where I: IntoIterator<Item=(K, V)>, K: AsRef<OsStr>, V: AsRef<OsStr>
89 {
90 for (ref key, ref val) in vars {
91 self.init_env_map();
92 self.environ.as_mut().unwrap().insert(
93 key.as_ref().to_os_string(),
94 val.as_ref().to_os_string());
95 self.pid_env_vars.remove(key.as_ref());
96 }
97 self
98 }
99
100 /// Removes an environment variable mapping.
101 pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Command {
102 self.init_env_map();
103 self.environ.as_mut().unwrap().remove(key.as_ref());
104 self.pid_env_vars.remove(key.as_ref());
105 self
106 }
107
108 /// Clears the entire environment map for the child process.
109 pub fn env_clear(&mut self) -> &mut Command {
110 self.environ = Some(HashMap::new());
111 self.pid_env_vars = HashSet::new();
112 self
113 }
114
115 /// Sets the working directory for the child process.
116 ///
117 /// Note: in case of `chroot` or `pivot_root` the working directory is
118 /// always set to something inside the new root. Algorithm is following:
119 ///
120 /// 1. If path is set to absolute path, current dir is this path *inside*
121 /// the chroot
122 /// 2. Check if chroot dir is prefix of `env::current_dir()`. If it is
123 /// set current directory to the suffix. Otherwise set current directory
124 /// to the new root dir.
125 /// 3. If `current_dir` is specified (and relative) set working directory
126 /// to the value (i.e. relative to the dir set in #2)
127 ///
128 /// The `pivot_root` is treated just the same as `chroot`. I.e. we will
129 /// not try to set working directory inside the `old_root`, unless path
130 /// inside is set explicitly by this method.
131 ///
132 /// At the end of the day, the ``cmd.current_dir(env::current_dir())`` is
133 /// not no-op if using chroot/pivot_root.
134 pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command
135 {
136 self.config.work_dir = Some(dir.as_ref().to_cstring());
137 self
138 }
139
140 /// Configuration for the child process's stdin handle (file descriptor 0).
141 pub fn stdin(&mut self, cfg: Stdio) -> &mut Command {
142 self.fds.insert(0, cfg.to_fd(false));
143 self
144 }
145
146 /// Configuration for the child process's stdout handle (file descriptor 1).
147 pub fn stdout(&mut self, cfg: Stdio) -> &mut Command {
148 self.fds.insert(1, cfg.to_fd(true));
149 self
150 }
151
152 /// Configuration for the child process's stderr handle (file descriptor 2).
153 pub fn stderr(&mut self, cfg: Stdio) -> &mut Command {
154 self.fds.insert(2, cfg.to_fd(true));
155 self
156 }
157
158 /// Set user id of the new process. Note that it works only for root
159 /// process or if you also set up user namespace
160 pub fn uid(&mut self, id: uid_t) -> &mut Command {
161 self.config.uid = Some(id);
162 self
163 }
164
165 /// Set primary group id of the new process. Note that it works only for
166 /// root process or if you also set up user namespace
167 pub fn gid(&mut self, id: gid_t) -> &mut Command {
168 self.config.gid = Some(id);
169 self
170 }
171
172 /// Set supplementary group ids. Note that it works only for root process
173 /// or if you also set up user namespace
174 pub fn groups(&mut self, ids: Vec<gid_t>) -> &mut Command {
175 self.config.supplementary_gids = Some(ids);
176 self
177 }
178}
179