Skip to content

Commit 95164f4

Browse files
committed
allow different arguments, fix environment variables
1 parent 313c41f commit 95164f4

File tree

6 files changed

+149
-59
lines changed

6 files changed

+149
-59
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ container and mount itself as a fuse filesystem.
2929
* apparamor/selinux
3030
* capabilities
3131
* user/group ids
32+
* environment variables
33+
* the following files: /etc/passwd, /etc/hostname, /etc/hosts, /etc/resolv.conf
3234

3335
## Usage
3436

src/attach/child.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use types::{Error, Result};
2020
use void::Void;
2121

2222
pub struct ChildOptions<'a> {
23+
pub command: Option<String>,
24+
pub arguments: Vec<String>,
2325
pub container_pid: Pid,
2426
pub mount_ready_sock: &'a ipc::Socket,
2527
pub fs: fs::CntrFs,
@@ -55,7 +57,15 @@ pub fn run(options: &ChildOptions) -> Result<Void> {
5557
"failed to change cgroup"
5658
);
5759

58-
let cmd = tryfmt!(Cmd::new(options.container_pid, options.home), "");
60+
let cmd = tryfmt!(
61+
Cmd::new(
62+
options.command.clone(),
63+
options.arguments.clone(),
64+
options.container_pid,
65+
options.home,
66+
),
67+
""
68+
);
5969

6070
let supported_namespaces = tryfmt!(
6171
namespace::supported_namespaces(),

src/attach/mod.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ mod child;
1313
mod parent;
1414

1515
pub struct AttachOptions {
16+
pub command: Option<String>,
17+
pub arguments: Vec<String>,
1618
pub container_name: String,
1719
pub container_types: Vec<Box<container::Container>>,
1820
pub effective_user: Option<pwd::Passwd>,
@@ -66,15 +68,17 @@ pub fn attach(opts: &AttachOptions) -> Result<Void> {
6668
match tryfmt!(unistd::fork(), "failed to fork") {
6769
ForkResult::Parent { child } => parent::run(child, &parent_sock, cntrfs),
6870
ForkResult::Child => {
69-
let opts = child::ChildOptions {
71+
let child_opts = child::ChildOptions {
72+
command: opts.command.clone(),
73+
arguments: opts.arguments.clone(),
7074
container_pid,
7175
mount_ready_sock: &child_sock,
7276
uid: container_uid,
7377
gid: container_gid,
7478
fs: cntrfs,
7579
home,
7680
};
77-
child::run(&opts)
81+
child::run(&child_opts)
7882
}
7983
}
8084
}

src/bin/main.rs

+52-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ extern crate argparse;
22
extern crate cntr;
33
extern crate nix;
44

5-
use argparse::{ArgumentParser, Store, List};
5+
use argparse::{ArgumentParser, Store, List, Collect};
66
use cntr::pwd::pwnam;
77
use std::io::{stdout, stderr};
88
use std::process;
@@ -28,13 +28,16 @@ impl FromStr for Command {
2828

2929
fn parse_attach_args(args: Vec<String>) -> cntr::AttachOptions {
3030
let mut options = cntr::AttachOptions {
31+
command: None,
32+
arguments: vec![],
3133
container_name: String::from(""),
3234
container_types: vec![],
3335
effective_user: None,
3436
};
3537
let mut container_type = String::from("");
3638
let mut container_name = String::from("");
3739
let mut effective_username = String::from("");
40+
let mut command = String::from("");
3841
{
3942
let mut ap = ArgumentParser::new();
4043
ap.set_description("Enter container");
@@ -46,13 +49,23 @@ fn parse_attach_args(args: Vec<String>) -> cntr::AttachOptions {
4649
ap.refer(&mut container_type).add_option(
4750
&["--type"],
4851
Store,
49-
"Container type (docker|lxc|rkt|process_id|nspawn)",
52+
"Container type (docker|lxc|rkt|process_id|nspawn, default: all)",
5053
);
51-
ap.refer(&mut container_name).add_argument(
54+
ap.refer(&mut container_name).required().add_argument(
5255
"id",
5356
Store,
5457
"container id, container name or process id",
5558
);
59+
ap.refer(&mut command).add_argument(
60+
"command",
61+
Store,
62+
"command to execute after attach (default: $SHELL)",
63+
);
64+
ap.refer(&mut options.arguments).add_argument(
65+
"arguments",
66+
Collect,
67+
"arguments passed to command",
68+
);
5669
match ap.parse(args, &mut stdout(), &mut stderr()) {
5770
Ok(()) => {}
5871
Err(x) => {
@@ -95,6 +108,10 @@ fn parse_attach_args(args: Vec<String>) -> cntr::AttachOptions {
95108
};
96109
}
97110

111+
if command != "" {
112+
options.command = Some(command);
113+
}
114+
98115
options
99116
}
100117

@@ -106,8 +123,36 @@ fn attach_command(args: Vec<String>) {
106123
};
107124
}
108125

109-
fn exec_command(args: &[String]) {
110-
if let Err(err) = cntr::exec(&args[1], &args[2..]) {
126+
fn exec_command(args: Vec<String>) {
127+
let mut command = String::from("");
128+
let mut arguments = vec![];
129+
{
130+
let mut ap = ArgumentParser::new();
131+
ap.set_description("Execute command in container filesystem");
132+
ap.refer(&mut command).add_argument(
133+
&"command",
134+
Store,
135+
"command to execute (default: $SHELL)",
136+
);
137+
ap.refer(&mut arguments).add_argument(
138+
&"arguments",
139+
List,
140+
"Arguments to pass to command",
141+
);
142+
match ap.parse(args, &mut stdout(), &mut stderr()) {
143+
Ok(()) => {}
144+
Err(x) => {
145+
std::process::exit(x);
146+
}
147+
}
148+
}
149+
let command = if command.is_empty() {
150+
None
151+
} else {
152+
Some(command)
153+
};
154+
155+
if let Err(err) = cntr::exec(command, arguments) {
111156
eprintln!("{}", err);
112157
process::exit(1);
113158
}
@@ -119,7 +164,7 @@ fn main() {
119164
{
120165
let mut ap = ArgumentParser::new();
121166
ap.set_description("Enter or executed in container");
122-
ap.refer(&mut subcommand).add_argument(
167+
ap.refer(&mut subcommand).required().add_argument(
123168
"command",
124169
Store,
125170
r#"Command to run (either "attach" or "exec")"#,
@@ -138,6 +183,6 @@ fn main() {
138183

139184
match subcommand {
140185
Command::attach => attach_command(args),
141-
Command::exec => exec_command(&args),
186+
Command::exec => exec_command(args),
142187
}
143188
}

src/cmd.rs

+71-14
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
1-
use nix::unistd;
1+
use libc;
2+
use nix::{self, unistd};
23
use std::collections::HashMap;
34
use std::env;
4-
use std::ffi::{CStr, OsStr, OsString};
5+
use std::ffi::{CStr, CString, OsStr, OsString};
56
use std::fs::File;
7+
use std::io;
68
use std::io::{BufRead, BufReader};
79
use std::os::unix::ffi::{OsStringExt, OsStrExt};
10+
use std::os::unix::process::CommandExt;
811
use std::path::PathBuf;
912
use std::process::{Command, ExitStatus};
1013
use types::{Error, Result};
1114

1215
pub struct Cmd {
1316
environment: HashMap<OsString, OsString>,
17+
command: String,
18+
arguments: Vec<String>,
19+
home: Option<CString>,
1420
}
1521

1622
fn read_environment(pid: unistd::Pid) -> Result<HashMap<OsString, OsString>> {
@@ -32,7 +38,7 @@ fn read_environment(pid: unistd::Pid) -> Result<HashMap<OsString, OsString>> {
3238
Err(_) => return None,
3339
};
3440

35-
let tuple: Vec<&[u8]> = var.splitn(1, |b| *b == b'=').collect();
41+
let tuple: Vec<&[u8]> = var.splitn(2, |b| *b == b'=').collect();
3642
if tuple.len() != 2 {
3743
return None;
3844
}
@@ -46,32 +52,83 @@ fn read_environment(pid: unistd::Pid) -> Result<HashMap<OsString, OsString>> {
4652
}
4753

4854
impl Cmd {
49-
pub fn new(pid: unistd::Pid, home: Option<&CStr>) -> Result<Cmd> {
50-
let mut variables = tryfmt!(
55+
pub fn new(
56+
command: Option<String>,
57+
args: Vec<String>,
58+
pid: unistd::Pid,
59+
home: Option<&CStr>,
60+
) -> Result<Cmd> {
61+
let command =
62+
command.unwrap_or_else(|| env::var("SHELL").unwrap_or_else(|_| String::from("sh")));
63+
let arguments = if args.is_empty() {
64+
vec![String::from("-l")]
65+
} else {
66+
args
67+
};
68+
69+
let variables = tryfmt!(
5170
read_environment(pid),
5271
"could not inherit environment variables of container"
5372
);
73+
Ok(Cmd {
74+
command,
75+
arguments: arguments,
76+
environment: variables,
77+
home: home.map(|h| h.to_owned()),
78+
})
79+
}
80+
pub fn run(mut self) -> Result<ExitStatus> {
5481
let default_path = OsString::from(
5582
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
5683
);
57-
variables.insert(
84+
self.environment.insert(
5885
OsString::from("PATH"),
5986
env::var_os("PATH").unwrap_or(default_path),
6087
);
61-
if let Some(path) = home {
62-
variables.insert(
88+
89+
if let Some(path) = self.home {
90+
self.environment.insert(
6391
OsString::from("HOME"),
6492
OsStr::from_bytes(path.to_bytes()).to_os_string(),
6593
);
6694
}
67-
Ok(Cmd { environment: variables })
68-
}
69-
pub fn run(self) -> Result<ExitStatus> {
70-
let shell = env::var("SHELL").unwrap_or_else(|_| String::from("sh"));
71-
let cmd = Command::new(shell)
72-
.args(&["-l"])
95+
96+
let cmd = Command::new(self.command)
97+
.args(self.arguments)
7398
.envs(self.environment)
7499
.status();
75100
Ok(tryfmt!(cmd, "failed to run `sh -l`"))
76101
}
102+
103+
pub fn exec_chroot(self) -> Result<()> {
104+
let err = Command::new(&self.command)
105+
.args(self.arguments)
106+
.envs(self.environment)
107+
.before_exec(|| {
108+
match unistd::chroot("/var/lib/cntr") {
109+
Err(nix::Error::Sys(errno)) => {
110+
warn!(
111+
"failed to chroot to /var/lib/cntr: {}",
112+
nix::Error::Sys(errno)
113+
);
114+
return Err(io::Error::from(errno));
115+
}
116+
Err(e) => {
117+
warn!("failed to chroot to /var/lib/cntr: {}", e);
118+
return Err(io::Error::from_raw_os_error(libc::EINVAL));
119+
}
120+
_ => {}
121+
}
122+
123+
if let Err(e) = env::set_current_dir("/") {
124+
warn!("failed to change directory to /");
125+
return Err(e);
126+
}
127+
128+
Ok(())
129+
})
130+
.exec();
131+
tryfmt!(Err(err), "failed to execute `{}`", self.command);
132+
Ok(())
133+
}
77134
}

src/exec.rs

+7-35
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,11 @@
1-
use libc;
2-
use nix::{self, unistd};
3-
use std::env;
4-
use std::io;
5-
use std::os::unix::process::CommandExt;
6-
use std::process::Command;
7-
use types::{Error, Result};
8-
91

10-
pub fn exec(exe: &String, args: &[String]) -> Result<()> {
11-
let err = Command::new(exe)
12-
.args(args)
13-
.before_exec(|| {
14-
match unistd::chroot("/var/lib/cntr") {
15-
Err(nix::Error::Sys(errno)) => {
16-
warn!(
17-
"failed to chroot to /var/lib/cntr: {}",
18-
nix::Error::Sys(errno)
19-
);
20-
return Err(io::Error::from(errno));
21-
}
22-
Err(e) => {
23-
warn!("failed to chroot to /var/lib/cntr: {}", e);
24-
return Err(io::Error::from_raw_os_error(libc::EINVAL));
25-
}
26-
_ => {}
27-
}
28-
29-
if let Err(e) = env::set_current_dir("/") {
30-
warn!("failed to change directory to /");
31-
return Err(e);
32-
}
2+
use cmd::Cmd;
3+
use nix::unistd::Pid;
4+
use types::{Error, Result};
335

34-
Ok(())
35-
})
36-
.exec();
37-
tryfmt!(Err(err), "failed to execute `{}`", exe);
6+
pub fn exec(exe: Option<String>, args: Vec<String>) -> Result<()> {
7+
println!("$ {:?} {}", exe, args.join(" "));
8+
let cmd = tryfmt!(Cmd::new(exe, args, Pid::from_raw(1), None), "");
9+
tryfmt!(cmd.exec_chroot(), "failed to execute command in container");
3810
Ok(())
3911
}

0 commit comments

Comments
 (0)