Skip to content

Commit f6594a1

Browse files
authored
Merge pull request #162 from Burning1020/runc-new-api
runc: support new sandbox api
2 parents 91e35df + 05f2a7d commit f6594a1

File tree

11 files changed

+764
-151
lines changed

11 files changed

+764
-151
lines changed

runc/Cargo.lock

Lines changed: 469 additions & 39 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

runc/Cargo.toml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

8+
[build-dependencies]
9+
built = { version = "0.7.0", features = ["cargo-lock", "dependency-tree", "git2", "chrono", "semver"] }
10+
811
[dependencies]
912
env_logger = "0.9.0"
1013
anyhow = { version = "=1.0.66", default-features = false, features = ["std"] }
1114
tokio = { version = "1.19.2", features = ["full"] }
1215
signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] }
13-
async-trait = "0.1.51"
14-
nix = "0.25"
16+
async-trait = "0.1.81"
17+
nix = { version = "0.28.0", features = ["fs", "mount", "socket", "process", "sched", "signal", "term"] }
1518
futures = { version = "0.3.21" }
1619
log = { version = "0.4.17", features = ["std"] }
1720
oci-spec = "0.5.4"
@@ -24,7 +27,9 @@ os_pipe = "1.1.4"
2427
byteorder = "1.4.3"
2528
go-flag = "0.1.0"
2629
uuid = { version = "1.1.2", features = ["v4"] }
30+
clap = { version = "4.5.4", features = ["derive"] }
31+
built = { version = "0.7.0", features = ["cargo-lock", "dependency-tree", "git2", "chrono", "semver"] }
2732

28-
containerd-sandbox = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5" }
29-
containerd-shim = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5", features = ["async"] }
30-
runc = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5", features = ["async"] }
33+
containerd-sandbox = { git = "https://github.com/kuasar-io/rust-extensions.git" }
34+
containerd-shim = { git = "https://github.com/kuasar-io/rust-extensions.git", features = ["async"] }
35+
runc = { git = "https://github.com/kuasar-io/rust-extensions.git", features = ["async"] }

runc/build.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
Copyright 2024 The Kuasar Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
use std::process::exit;
18+
19+
fn main() {
20+
if let Err(e) = built::write_built_file() {
21+
eprint!("Failed to acquire build-time information: {:?}", e);
22+
exit(-1)
23+
}
24+
}

runc/rustfmt.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
newline_style = "Unix"
2+
unstable_features = true
3+
group_imports = "StdExternalCrate"
4+
imports_granularity = "Crate"

runc/src/args.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
Copyright 2024 The Kuasar Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
use clap::Parser;
18+
19+
#[derive(Parser, Debug)]
20+
#[command(author, about, long_about = None)]
21+
pub struct Args {
22+
/// Version info
23+
#[arg(short, long)]
24+
pub version: bool,
25+
26+
/// Sandboxer working directory, default is `/run/kuasar-runc`
27+
#[arg(short, long, value_name = "DIR", default_value = "/run/kuasar-runc")]
28+
pub dir: String,
29+
30+
/// Address for sandboxer's server, default is `/run/runc-sandboxer.sock`
31+
#[arg(
32+
short,
33+
long,
34+
value_name = "FILE",
35+
default_value = "/run/runc-sandboxer.sock"
36+
)]
37+
pub listen: String,
38+
39+
// log_level is optional and should not have default value if not given, since
40+
// it can be defined in configuration file.
41+
/// Logging level for sandboxer [trace, debug, info, warn, error, fatal, panic]
42+
#[arg(long, value_name = "STRING")]
43+
pub log_level: Option<String>,
44+
}
45+
46+
#[cfg(test)]
47+
mod tests {
48+
use clap::Parser;
49+
50+
use crate::args::Args;
51+
52+
#[test]
53+
fn test_args_parse_default() {
54+
let args = Args::parse();
55+
assert!(!args.version);
56+
assert_eq!(args.dir, "/run/kuasar-runc");
57+
assert_eq!(args.listen, "/run/runc-sandboxer.sock");
58+
assert!(args.log_level.is_none());
59+
}
60+
}

runc/src/common.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,16 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
use std::{io::IoSliceMut, ops::Deref, os::unix::io::RawFd, path::Path, sync::Arc};
17+
use std::{
18+
io::IoSliceMut,
19+
ops::Deref,
20+
os::{
21+
fd::{FromRawFd, OwnedFd},
22+
unix::io::RawFd,
23+
},
24+
path::Path,
25+
sync::Arc,
26+
};
1827

1928
use anyhow::anyhow;
2029
use containerd_shim::{
@@ -164,7 +173,7 @@ pub fn create_runc(
164173
#[derive(Default)]
165174
pub(crate) struct CreateConfig {}
166175

167-
pub fn receive_socket(stream_fd: RawFd) -> containerd_shim::Result<RawFd> {
176+
pub fn receive_socket(stream_fd: RawFd) -> containerd_shim::Result<OwnedFd> {
168177
let mut buf = [0u8; 4096];
169178
let mut iovec = [IoSliceMut::new(&mut buf)];
170179
let mut space = cmsg_space!([RawFd; 2]);
@@ -194,8 +203,9 @@ pub fn receive_socket(stream_fd: RawFd) -> containerd_shim::Result<RawFd> {
194203
"copy_console: console socket get path: {}, fd: {}",
195204
path, &fds[0]
196205
);
197-
tcgetattr(fds[0])?;
198-
Ok(fds[0])
206+
let fd = unsafe { OwnedFd::from_raw_fd(fds[0]) };
207+
tcgetattr(&fd)?;
208+
Ok(fd)
199209
}
200210

201211
pub fn has_shared_pid_namespace(spec: &Spec) -> bool {

runc/src/main.rs

Lines changed: 68 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,51 +14,71 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
use std::ffi::CString;
18-
use std::os::fd::RawFd;
19-
use std::process::exit;
17+
use std::{
18+
ffi::CString,
19+
os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd},
20+
path::Path,
21+
process::exit,
22+
str::FromStr,
23+
};
2024

2125
use anyhow::anyhow;
26+
use clap::Parser;
2227
use containerd_shim::asynchronous::monitor::monitor_notify_by_pid;
2328
use futures::StreamExt;
24-
use log::{debug, error, warn};
25-
use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag};
26-
use nix::sched::{setns, unshare, CloneFlags};
27-
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGCHLD};
28-
use nix::sys::stat::Mode;
29-
use nix::unistd::{close, fork, pause, pipe, read, write, ForkResult};
29+
use log::{debug, error, warn, LevelFilter};
3030
use nix::{
3131
errno::Errno,
32+
fcntl::{fcntl, FcntlArg, FdFlag, OFlag},
3233
libc,
34+
sched::{setns, unshare, CloneFlags},
3335
sys::{
36+
signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGCHLD},
37+
stat::Mode,
3438
wait,
3539
wait::{WaitPidFlag, WaitStatus},
3640
},
37-
unistd::Pid,
41+
unistd::{fork, pause, pipe, read, write, ForkResult, Pid},
3842
};
3943
use prctl::PrctlMM;
4044
use signal_hook_tokio::Signals;
4145
use uuid::Uuid;
4246

43-
use crate::sandbox::{RuncSandboxer, SandboxParent};
44-
use crate::task::fork_task_server;
47+
use crate::{
48+
sandbox::{RuncSandboxer, SandboxParent},
49+
task::fork_task_server,
50+
};
4551

52+
mod args;
4653
mod common;
4754
mod runc;
4855
mod sandbox;
4956
mod task;
57+
mod version;
5058

5159
fn main() {
52-
env_logger::builder().format_timestamp_micros().init();
60+
let args = args::Args::parse();
61+
if args.version {
62+
version::print_version_info();
63+
return;
64+
}
65+
66+
// Update args log level if it not presents args but in config.
67+
let log_level =
68+
LevelFilter::from_str(&args.log_level.unwrap_or_default()).unwrap_or(LevelFilter::Info);
69+
env_logger::Builder::from_default_env()
70+
.format_timestamp_micros()
71+
.filter_module("containerd_sandbox", log_level)
72+
.filter_module("runc_sandboxer", log_level)
73+
.init();
74+
5375
let sandbox_parent = fork_sandbox_parent().unwrap();
54-
let os_args: Vec<_> = std::env::args_os().collect();
55-
// TODO avoid parse args multiple times
56-
let flags = containerd_sandbox::args::parse(&os_args[1..]).unwrap();
57-
let task_socket = format!("{}/task-{}.sock", flags.dir, Uuid::new_v4());
58-
fork_task_server(&task_socket, &flags.dir).unwrap();
76+
77+
let task_socket = format!("{}/task-{}.sock", &args.dir, Uuid::new_v4());
78+
fork_task_server(&task_socket, &args.dir).unwrap();
5979
let runtime = tokio::runtime::Runtime::new().unwrap();
6080
runtime.block_on(async move {
61-
start_sandboxer(sandbox_parent, task_socket, flags.dir)
81+
start_sandboxer(sandbox_parent, task_socket, &args.listen, &args.dir)
6282
.await
6383
.unwrap();
6484
});
@@ -75,12 +95,12 @@ fn fork_sandbox_parent() -> Result<SandboxParent, anyhow::Error> {
7595
match unsafe { fork().map_err(|e| anyhow!("failed to fork sandbox parent {}", e))? } {
7696
ForkResult::Parent { child } => {
7797
debug!("forked process {} for the sandbox parent", child);
78-
close(reqr).unwrap_or_default();
79-
close(respw).unwrap_or_default();
98+
drop(reqr);
99+
drop(respw);
80100
}
81101
ForkResult::Child => {
82-
close(reqw).unwrap_or_default();
83-
close(respr).unwrap_or_default();
102+
drop(reqw);
103+
drop(respr);
84104
prctl::set_child_subreaper(true).unwrap();
85105
let comm = "[sandbox-parent]";
86106
let comm_cstr = CString::new(comm).unwrap();
@@ -95,7 +115,7 @@ fn fork_sandbox_parent() -> Result<SandboxParent, anyhow::Error> {
95115
sigaction(SIGCHLD, &sig_action).unwrap();
96116
}
97117
loop {
98-
let buffer = read_count(reqr, 512).unwrap();
118+
let buffer = read_count(reqr.as_raw_fd(), 512).unwrap();
99119
let id = String::from_utf8_lossy(&buffer[0..64]).to_string();
100120
let mut zero_index = 64;
101121
for (i, &b) in buffer.iter().enumerate().take(512).skip(64) {
@@ -106,12 +126,12 @@ fn fork_sandbox_parent() -> Result<SandboxParent, anyhow::Error> {
106126
}
107127
let netns = String::from_utf8_lossy(&buffer[64..zero_index]).to_string();
108128
let sandbox_pid = fork_sandbox(&id, &netns).unwrap();
109-
write_all(respw, sandbox_pid.to_le_bytes().as_slice()).unwrap();
129+
write_all(&respw, sandbox_pid.to_le_bytes().as_slice()).unwrap();
110130
}
111131
}
112132
}
113-
fcntl(reqw, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)).unwrap_or_default();
114-
fcntl(respr, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)).unwrap_or_default();
133+
fcntl(reqw.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)).unwrap_or_default();
134+
fcntl(respr.as_raw_fd(), FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)).unwrap_or_default();
115135
Ok(SandboxParent::new(reqw, respr))
116136
}
117137

@@ -136,7 +156,7 @@ pub fn read_count(fd: RawFd, count: usize) -> Result<Vec<u8>, anyhow::Error> {
136156
}
137157
}
138158

139-
pub fn write_all(fd: RawFd, buf: &[u8]) -> Result<(), anyhow::Error> {
159+
pub fn write_all(fd: &OwnedFd, buf: &[u8]) -> Result<(), anyhow::Error> {
140160
let mut idx = 0;
141161
let count = buf.len();
142162
loop {
@@ -162,21 +182,21 @@ fn fork_sandbox(id: &str, netns: &str) -> Result<i32, anyhow::Error> {
162182
match unsafe { fork().map_err(|e| anyhow!("failed to fork sandbox {}", e))? } {
163183
ForkResult::Parent { child } => {
164184
debug!("forked process {} for the sandbox {}", child, id);
165-
close(w).unwrap_or_default();
185+
drop(w);
166186
let mut resp = [0u8; 4];
167-
let r = read_count(r, 4)?;
187+
let r = read_count(r.as_raw_fd(), 4)?;
168188
resp[..].copy_from_slice(r.as_slice());
169189
let pid = i32::from_le_bytes(resp);
170190
Ok(pid)
171191
}
172192
ForkResult::Child => {
173-
close(r).unwrap_or_default();
193+
drop(r);
174194
unshare(CloneFlags::CLONE_NEWIPC | CloneFlags::CLONE_NEWUTS | CloneFlags::CLONE_NEWPID)
175195
.unwrap();
176196
match unsafe { fork().unwrap() } {
177197
ForkResult::Parent { child } => {
178198
debug!("forked process {} for the sandbox {}", child, id);
179-
write_all(w, child.as_raw().to_le_bytes().as_slice()).unwrap();
199+
write_all(&w, child.as_raw().to_le_bytes().as_slice()).unwrap();
180200
exit(0);
181201
}
182202
ForkResult::Child => {
@@ -186,7 +206,8 @@ fn fork_sandbox(id: &str, netns: &str) -> Result<i32, anyhow::Error> {
186206
set_process_comm(addr as u64, comm_cstr.as_bytes_with_nul().len() as u64);
187207
if !netns.is_empty() {
188208
let netns_fd =
189-
nix::fcntl::open(netns, OFlag::O_CLOEXEC, Mode::empty()).unwrap();
209+
safe_open_file(Path::new(&netns), OFlag::O_CLOEXEC, Mode::empty())
210+
.unwrap();
190211
setns(netns_fd, CloneFlags::CLONE_NEWNET).unwrap();
191212
}
192213
loop {
@@ -198,6 +219,16 @@ fn fork_sandbox(id: &str, netns: &str) -> Result<i32, anyhow::Error> {
198219
}
199220
}
200221

222+
pub fn safe_open_file<P: ?Sized + nix::NixPath>(
223+
path: &P,
224+
oflag: OFlag,
225+
mode: Mode,
226+
) -> Result<OwnedFd, nix::Error> {
227+
let fd = nix::fcntl::open(path, oflag, mode)?;
228+
// SAFETY: contruct a OwnedFd from RawFd, close fd when OwnedFd drop
229+
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
230+
}
231+
201232
fn set_process_comm(addr: u64, len: u64) {
202233
if prctl::set_mm(PrctlMM::PR_SET_MM_ARG_START, addr).is_err() {
203234
prctl::set_mm(PrctlMM::PR_SET_MM_ARG_END, addr + len).unwrap();
@@ -230,12 +261,13 @@ extern "C" fn sandbox_parent_handle_signals(_: libc::c_int) {
230261
async fn start_sandboxer(
231262
sandbox_parent: SandboxParent,
232263
task_socket: String,
233-
dir: String,
264+
listen: &str,
265+
dir: &str,
234266
) -> anyhow::Result<()> {
235267
let task_address = format!("unix://{}", task_socket);
236268
let sandboxer = RuncSandboxer::new(sandbox_parent, &task_address).await?;
237-
sandboxer.recover(&dir).await?;
238-
containerd_sandbox::run("runc-sandboxer", sandboxer).await?;
269+
sandboxer.recover(dir).await?;
270+
containerd_sandbox::run("kuasar-runc-sandboxer", listen, dir, sandboxer).await?;
239271
Ok(())
240272
}
241273

0 commit comments

Comments
 (0)