projects/aports·blob master · refs

549 lines · 23.4 KiB

1Patch-Source: https://github.com/youki-dev/youki/pull/3146
2--
3From d8198befdf89e33e28425cc334a342a73e725043 Mon Sep 17 00:00:00 2001
4From: Michal Rostecki <[email protected]>
5Date: Fri, 11 Apr 2025 07:41:31 +0200
6Subject: [PATCH] libcontainer: Skip cgroup creation for rootless containers
7 with cgroupfs
8
9Creating cgroups with cgroupfs as non-root is not possible. Before this
10change, we used to enforce systemd as a cgroup manager for all rootless
11containers, but that prevents the usage of rootless youki on systems
12wuthout systemd. To make such usage possible, raise a warning and skip
13cgroup-related activities instead. That matches the behavior of crun
14(containers/crun#97).
15
16Fixes #3144
17
18Signed-off-by: Michal Rostecki <[email protected]>
19---
20 crates/libcgroups/src/common.rs | 19 ++++--
21 .../src/container/builder_impl.rs | 14 +++--
22 .../libcontainer/src/container/container.rs | 9 +++
23 .../src/container/container_delete.rs | 5 +-
24 .../src/container/container_events.rs | 37 ++++++-----
25 .../src/container/container_kill.rs | 60 ++++++++++--------
26 .../src/container/container_pause.rs | 5 +-
27 .../src/container/container_resume.rs | 5 +-
28 .../src/container/init_builder.rs | 1 +
29 crates/libcontainer/src/container/state.rs | 3 +
30 .../process/container_intermediate_process.rs | 12 ++--
31 crates/youki/src/commands/mod.rs | 3 +-
32 crates/youki/src/commands/ps.rs | 62 ++++++++++---------
33 crates/youki/src/commands/update.rs | 42 +++++++------
34 14 files changed, 163 insertions(+), 114 deletions(-)
35
36diff --git a/crates/libcgroups/src/common.rs b/crates/libcgroups/src/common.rs
37index 76bcc14f00..9148326d76 100644
38--- a/crates/libcgroups/src/common.rs
39+++ b/crates/libcgroups/src/common.rs
40@@ -330,6 +330,7 @@ pub enum CreateCgroupSetupError {
41 pub struct CgroupConfig {
42 pub cgroup_path: PathBuf,
43 pub systemd_cgroup: bool,
44+ pub rootless_container: bool,
45 pub container_name: String,
46 }
47
48@@ -338,7 +339,13 @@ pub struct CgroupConfig {
49 pub fn create_cgroup_manager_with_root(
50 root_path: Option<&Path>,
51 config: CgroupConfig,
52-) -> Result<AnyCgroupManager, CreateCgroupSetupError> {
53+) -> Result<Option<AnyCgroupManager>, CreateCgroupSetupError> {
54+ // Creating cgroups with cgroupfs as non-root is not possible.
55+ if !config.systemd_cgroup && config.rootless_container {
56+ tracing::warn!("cannot configure rootless cgroup using the cgroupfs manager");
57+ return Ok(None);
58+ }
59+
60 let root = match root_path {
61 Some(p) => p,
62 None => Path::new(DEFAULT_CGROUP_ROOT),
63@@ -353,24 +360,24 @@ pub fn create_cgroup_manager_with_root(
64
65 match cgroup_setup {
66 CgroupSetup::Legacy | CgroupSetup::Hybrid => {
67- Ok(create_v1_cgroup_manager(cgroup_path)?.any())
68+ Ok(Some(create_v1_cgroup_manager(cgroup_path)?.any()))
69 }
70 CgroupSetup::Unified => {
71 // ref https://github.com/opencontainers/runtime-spec/blob/main/config-linux.md#cgroups-path
72 if cgroup_path.is_absolute() || !config.systemd_cgroup {
73- return Ok(create_v2_cgroup_manager(root, cgroup_path)?.any());
74+ return Ok(Some(create_v2_cgroup_manager(root, cgroup_path)?.any()));
75 }
76- Ok(
77+ Ok(Some(
78 create_systemd_cgroup_manager(root, cgroup_path, config.container_name.as_str())?
79 .any(),
80- )
81+ ))
82 }
83 }
84 }
85
86 pub fn create_cgroup_manager(
87 config: CgroupConfig,
88-) -> Result<AnyCgroupManager, CreateCgroupSetupError> {
89+) -> Result<Option<AnyCgroupManager>, CreateCgroupSetupError> {
90 create_cgroup_manager_with_root(Some(Path::new(DEFAULT_CGROUP_ROOT)), config)
91 }
92
93diff --git a/crates/libcontainer/src/container/builder_impl.rs b/crates/libcontainer/src/container/builder_impl.rs
94index 8d8494bd76..96f098ad33 100644
95--- a/crates/libcontainer/src/container/builder_impl.rs
96+++ b/crates/libcontainer/src/container/builder_impl.rs
97@@ -88,7 +88,8 @@ impl ContainerBuilderImpl {
98 let cgroups_path = utils::get_cgroup_path(linux.cgroups_path(), &self.container_id);
99 let cgroup_config = libcgroups::common::CgroupConfig {
100 cgroup_path: cgroups_path,
101- systemd_cgroup: self.use_systemd || self.user_ns_config.is_some(),
102+ systemd_cgroup: self.use_systemd,
103+ rootless_container: self.user_ns_config.is_some(),
104 container_name: self.container_id.to_owned(),
105 };
106 let process = self
107@@ -212,15 +213,18 @@ impl ContainerBuilderImpl {
108 let cmanager =
109 libcgroups::common::create_cgroup_manager(libcgroups::common::CgroupConfig {
110 cgroup_path: cgroups_path,
111- systemd_cgroup: self.use_systemd || self.user_ns_config.is_some(),
112+ systemd_cgroup: self.use_systemd,
113+ rootless_container: self.user_ns_config.is_some(),
114 container_name: self.container_id.to_string(),
115 })?;
116
117 let mut errors = Vec::new();
118
119- if let Err(e) = cmanager.remove() {
120- tracing::error!(error = ?e, "failed to remove cgroup manager");
121- errors.push(e.to_string());
122+ if let Some(cmanager) = cmanager {
123+ if let Err(e) = cmanager.remove() {
124+ tracing::error!(error = ?e, "failed to remove cgroup manager");
125+ errors.push(e.to_string());
126+ }
127 }
128
129 if let Some(container) = &self.container {
130diff --git a/crates/libcontainer/src/container/container.rs b/crates/libcontainer/src/container/container.rs
131index 7a05c3495f..2abe190cc1 100644
132--- a/crates/libcontainer/src/container/container.rs
133+++ b/crates/libcontainer/src/container/container.rs
134@@ -130,6 +130,15 @@ impl Container {
135 self
136 }
137
138+ pub fn rootless(&self) -> bool {
139+ self.state.is_rootless
140+ }
141+
142+ pub fn set_rootless(&mut self, is_rootless: bool) -> &mut Self {
143+ self.state.is_rootless = is_rootless;
144+ self
145+ }
146+
147 pub fn set_clean_up_intel_rdt_directory(&mut self, clean_up: bool) -> &mut Self {
148 self.state.clean_up_intel_rdt_subdirectory = Some(clean_up);
149 self
150diff --git a/crates/libcontainer/src/container/container_delete.rs b/crates/libcontainer/src/container/container_delete.rs
151index d246658c32..69aea6f537 100644
152--- a/crates/libcontainer/src/container/container_delete.rs
153+++ b/crates/libcontainer/src/container/container_delete.rs
154@@ -89,13 +89,16 @@ impl Container {
155 libcgroups::common::CgroupConfig {
156 cgroup_path: config.cgroup_path.to_owned(),
157 systemd_cgroup: self.systemd(),
158+ rootless_container: self.rootless(),
159 container_name: self.id().to_string(),
160 },
161 )?;
162- cmanager.remove().map_err(|err| {
163+ if let Some(cmanager) = cmanager {
164+ cmanager.remove().map_err(|err| {
165 tracing::error!(cgroup_path = ?config.cgroup_path, "failed to remove cgroup due to: {err:?}");
166 err
167 })?;
168+ }
169
170 if let Some(hooks) = config.hooks.as_ref() {
171 hooks::run_hooks(hooks.poststop().as_ref(), Some(self), None).map_err(
172diff --git a/crates/libcontainer/src/container/container_events.rs b/crates/libcontainer/src/container/container_events.rs
173index 83df282f4d..8ee0455356 100644
174--- a/crates/libcontainer/src/container/container_events.rs
175+++ b/crates/libcontainer/src/container/container_events.rs
176@@ -38,26 +38,29 @@ impl Container {
177 libcgroups::common::create_cgroup_manager(libcgroups::common::CgroupConfig {
178 cgroup_path: self.spec()?.cgroup_path,
179 systemd_cgroup: self.systemd(),
180+ rootless_container: self.rootless(),
181 container_name: self.id().to_string(),
182 })?;
183- match stats {
184- true => {
185- let stats = cgroup_manager.stats()?;
186- println!(
187- "{}",
188- serde_json::to_string_pretty(&stats)
189- .map_err(LibcontainerError::OtherSerialization)?
190- );
191+ if let Some(cgroup_manager) = cgroup_manager {
192+ match stats {
193+ true => {
194+ let stats = cgroup_manager.stats()?;
195+ println!(
196+ "{}",
197+ serde_json::to_string_pretty(&stats)
198+ .map_err(LibcontainerError::OtherSerialization)?
199+ );
200+ }
201+ false => loop {
202+ let stats = cgroup_manager.stats()?;
203+ println!(
204+ "{}",
205+ serde_json::to_string_pretty(&stats)
206+ .map_err(LibcontainerError::OtherSerialization)?
207+ );
208+ thread::sleep(Duration::from_secs(interval as u64));
209+ },
210 }
211- false => loop {
212- let stats = cgroup_manager.stats()?;
213- println!(
214- "{}",
215- serde_json::to_string_pretty(&stats)
216- .map_err(LibcontainerError::OtherSerialization)?
217- );
218- thread::sleep(Duration::from_secs(interval as u64));
219- },
220 }
221
222 Ok(())
223diff --git a/crates/libcontainer/src/container/container_kill.rs b/crates/libcontainer/src/container/container_kill.rs
224index fc5da3db9e..53faa74e6a 100644
225--- a/crates/libcontainer/src/container/container_kill.rs
226+++ b/crates/libcontainer/src/container/container_kill.rs
227@@ -86,10 +86,13 @@ impl Container {
228 libcgroups::common::CgroupConfig {
229 cgroup_path: self.spec()?.cgroup_path,
230 systemd_cgroup: self.systemd(),
231+ rootless_container: self.rootless(),
232 container_name: self.id().to_string(),
233 },
234 )?;
235- cmanager.freeze(libcgroups::common::FreezerState::Thawed)?;
236+ if let Some(cmanager) = cmanager {
237+ cmanager.freeze(libcgroups::common::FreezerState::Thawed)?;
238+ }
239 }
240 libcgroups::common::CgroupSetup::Unified => {}
241 }
242@@ -103,37 +106,40 @@ impl Container {
243 libcgroups::common::create_cgroup_manager(libcgroups::common::CgroupConfig {
244 cgroup_path: self.spec()?.cgroup_path,
245 systemd_cgroup: self.systemd(),
246+ rootless_container: self.rootless(),
247 container_name: self.id().to_string(),
248 })?;
249
250- if let Err(e) = cmanager.freeze(libcgroups::common::FreezerState::Frozen) {
251- tracing::warn!(
252- err = ?e,
253- id = ?self.id(),
254- "failed to freeze container",
255- );
256- }
257+ if let Some(cmanager) = cmanager {
258+ if let Err(e) = cmanager.freeze(libcgroups::common::FreezerState::Frozen) {
259+ tracing::warn!(
260+ err = ?e,
261+ id = ?self.id(),
262+ "failed to freeze container",
263+ );
264+ }
265
266- let pids = cmanager.get_all_pids()?;
267- pids.iter()
268- .try_for_each(|&pid| {
269- tracing::debug!("kill signal {} to {}", signal, pid);
270- let res = signal::kill(pid, signal);
271- match res {
272- Err(nix::errno::Errno::ESRCH) => {
273- // the process does not exist, which is what we want
274- Ok(())
275+ let pids = cmanager.get_all_pids()?;
276+ pids.iter()
277+ .try_for_each(|&pid| {
278+ tracing::debug!("kill signal {} to {}", signal, pid);
279+ let res = signal::kill(pid, signal);
280+ match res {
281+ Err(nix::errno::Errno::ESRCH) => {
282+ // the process does not exist, which is what we want
283+ Ok(())
284+ }
285+ _ => res,
286 }
287- _ => res,
288- }
289- })
290- .map_err(LibcontainerError::OtherSyscall)?;
291- if let Err(err) = cmanager.freeze(libcgroups::common::FreezerState::Thawed) {
292- tracing::warn!(
293- err = ?err,
294- id = ?self.id(),
295- "failed to thaw container",
296- );
297+ })
298+ .map_err(LibcontainerError::OtherSyscall)?;
299+ if let Err(err) = cmanager.freeze(libcgroups::common::FreezerState::Thawed) {
300+ tracing::warn!(
301+ err = ?err,
302+ id = ?self.id(),
303+ "failed to thaw container",
304+ );
305+ }
306 }
307
308 Ok(())
309diff --git a/crates/libcontainer/src/container/container_pause.rs b/crates/libcontainer/src/container/container_pause.rs
310index 7dcfa77936..50776e6b8d 100644
311--- a/crates/libcontainer/src/container/container_pause.rs
312+++ b/crates/libcontainer/src/container/container_pause.rs
313@@ -36,9 +36,12 @@ impl Container {
314 libcgroups::common::create_cgroup_manager(libcgroups::common::CgroupConfig {
315 cgroup_path: self.spec()?.cgroup_path,
316 systemd_cgroup: self.systemd(),
317+ rootless_container: self.rootless(),
318 container_name: self.id().to_string(),
319 })?;
320- cmanager.freeze(FreezerState::Frozen)?;
321+ if let Some(cmanager) = cmanager {
322+ cmanager.freeze(FreezerState::Frozen)?;
323+ }
324
325 tracing::debug!("saving paused status");
326 self.set_status(ContainerStatus::Paused).save()?;
327diff --git a/crates/libcontainer/src/container/container_resume.rs b/crates/libcontainer/src/container/container_resume.rs
328index 48fd65d849..0310c58109 100644
329--- a/crates/libcontainer/src/container/container_resume.rs
330+++ b/crates/libcontainer/src/container/container_resume.rs
331@@ -37,10 +37,13 @@ impl Container {
332 libcgroups::common::create_cgroup_manager(libcgroups::common::CgroupConfig {
333 cgroup_path: self.spec()?.cgroup_path,
334 systemd_cgroup: self.systemd(),
335+ rootless_container: self.rootless(),
336 container_name: self.id().to_string(),
337 })?;
338 // resume the frozen container
339- cmanager.freeze(FreezerState::Thawed)?;
340+ if let Some(cmanager) = cmanager {
341+ cmanager.freeze(FreezerState::Thawed)?;
342+ }
343
344 tracing::debug!("saving running status");
345 self.set_status(ContainerStatus::Running).save()?;
346diff --git a/crates/libcontainer/src/container/init_builder.rs b/crates/libcontainer/src/container/init_builder.rs
347index 86e728802d..64b7e741ad 100644
348--- a/crates/libcontainer/src/container/init_builder.rs
349+++ b/crates/libcontainer/src/container/init_builder.rs
350@@ -89,6 +89,7 @@ impl InitContainerBuilder {
351 };
352
353 let user_ns_config = UserNamespaceConfig::new(&spec)?;
354+ container.set_rootless(user_ns_config.is_some());
355
356 let config = YoukiConfig::from_spec(&spec, container.id())?;
357 config.save(&container_dir).map_err(|err| {
358diff --git a/crates/libcontainer/src/container/state.rs b/crates/libcontainer/src/container/state.rs
359index 4f0bc07aac..068f92920d 100644
360--- a/crates/libcontainer/src/container/state.rs
361+++ b/crates/libcontainer/src/container/state.rs
362@@ -114,6 +114,8 @@ pub struct State {
363 pub creator: Option<u32>,
364 // Specifies if systemd should be used to manage cgroups
365 pub use_systemd: bool,
366+ // Specifies if the container is rootless.
367+ pub is_rootless: bool,
368 // Specifies if the Intel RDT subdirectory needs be cleaned up.
369 pub clean_up_intel_rdt_subdirectory: Option<bool>,
370 }
371@@ -137,6 +139,7 @@ impl State {
372 created: None,
373 creator: None,
374 use_systemd: false,
375+ is_rootless: false,
376 clean_up_intel_rdt_subdirectory: None,
377 }
378 }
379diff --git a/crates/libcontainer/src/process/container_intermediate_process.rs b/crates/libcontainer/src/process/container_intermediate_process.rs
380index 1bee6f3eee..a2c3310f2a 100644
381--- a/crates/libcontainer/src/process/container_intermediate_process.rs
382+++ b/crates/libcontainer/src/process/container_intermediate_process.rs
383@@ -62,11 +62,13 @@ pub fn container_intermediate_process(
384 // In addition this needs to be done before we enter the cgroup namespace as
385 // the cgroup of the process will form the root of the cgroup hierarchy in
386 // the cgroup namespace.
387- apply_cgroups(
388- &cgroup_manager,
389- linux.resources().as_ref(),
390- matches!(args.container_type, ContainerType::InitContainer),
391- )?;
392+ if let Some(cgroup_manager) = cgroup_manager {
393+ apply_cgroups(
394+ &cgroup_manager,
395+ linux.resources().as_ref(),
396+ matches!(args.container_type, ContainerType::InitContainer),
397+ )?;
398+ }
399
400 // if new user is specified in specification, this will be true and new
401 // namespace will be created, check
402diff --git a/crates/youki/src/commands/mod.rs b/crates/youki/src/commands/mod.rs
403index 08044b7fbf..d7e84df147 100644
404--- a/crates/youki/src/commands/mod.rs
405+++ b/crates/youki/src/commands/mod.rs
406@@ -55,12 +55,13 @@ fn container_exists<P: AsRef<Path>>(root_path: P, container_id: &str) -> Result<
407 fn create_cgroup_manager<P: AsRef<Path>>(
408 root_path: P,
409 container_id: &str,
410-) -> Result<AnyCgroupManager> {
411+) -> Result<Option<AnyCgroupManager>> {
412 let container = load_container(root_path, container_id)?;
413 Ok(libcgroups::common::create_cgroup_manager(
414 libcgroups::common::CgroupConfig {
415 cgroup_path: container.spec()?.cgroup_path,
416 systemd_cgroup: container.systemd(),
417+ rootless_container: container.rootless(),
418 container_name: container.id().to_string(),
419 },
420 )?)
421diff --git a/crates/youki/src/commands/ps.rs b/crates/youki/src/commands/ps.rs
422index a195196ac9..4cf2d09040 100644
423--- a/crates/youki/src/commands/ps.rs
424+++ b/crates/youki/src/commands/ps.rs
425@@ -10,37 +10,39 @@ use crate::commands::create_cgroup_manager;
426 pub fn ps(args: Ps, root_path: PathBuf) -> Result<()> {
427 let cmanager = create_cgroup_manager(root_path, &args.container_id)?;
428
429- let pids: Vec<i32> = cmanager
430- .get_all_pids()?
431- .iter()
432- .map(|pid| pid.as_raw())
433- .collect();
434+ if let Some(cmanager) = cmanager {
435+ let pids: Vec<i32> = cmanager
436+ .get_all_pids()?
437+ .iter()
438+ .map(|pid| pid.as_raw())
439+ .collect();
440
441- if args.format == "json" {
442- println!("{}", serde_json::to_string(&pids)?);
443- } else if args.format == "table" {
444- let default_ps_options = vec![String::from("-ef")];
445- let ps_options = if args.ps_options.is_empty() {
446- &default_ps_options
447- } else {
448- &args.ps_options
449- };
450- let output = Command::new("ps").args(ps_options).output()?;
451- if !output.status.success() {
452- println!("{}", std::str::from_utf8(&output.stderr)?);
453- } else {
454- let lines = std::str::from_utf8(&output.stdout)?;
455- let lines: Vec<&str> = lines.split('\n').collect();
456- let pid_index = get_pid_index(lines[0])?;
457- println!("{}", &lines[0]);
458- for line in &lines[1..] {
459- if line.is_empty() {
460- continue;
461- }
462- let fields: Vec<&str> = line.split_whitespace().collect();
463- let pid: i32 = fields[pid_index].parse()?;
464- if pids.contains(&pid) {
465- println!("{line}");
466+ if args.format == "json" {
467+ println!("{}", serde_json::to_string(&pids)?);
468+ } else if args.format == "table" {
469+ let default_ps_options = vec![String::from("-ef")];
470+ let ps_options = if args.ps_options.is_empty() {
471+ &default_ps_options
472+ } else {
473+ &args.ps_options
474+ };
475+ let output = Command::new("ps").args(ps_options).output()?;
476+ if !output.status.success() {
477+ println!("{}", std::str::from_utf8(&output.stderr)?);
478+ } else {
479+ let lines = std::str::from_utf8(&output.stdout)?;
480+ let lines: Vec<&str> = lines.split('\n').collect();
481+ let pid_index = get_pid_index(lines[0])?;
482+ println!("{}", &lines[0]);
483+ for line in &lines[1..] {
484+ if line.is_empty() {
485+ continue;
486+ }
487+ let fields: Vec<&str> = line.split_whitespace().collect();
488+ let pid: i32 = fields[pid_index].parse()?;
489+ if pids.contains(&pid) {
490+ println!("{line}");
491+ }
492 }
493 }
494 }
495diff --git a/crates/youki/src/commands/update.rs b/crates/youki/src/commands/update.rs
496index 115c334274..d43e9fd05c 100644
497--- a/crates/youki/src/commands/update.rs
498+++ b/crates/youki/src/commands/update.rs
499@@ -12,28 +12,30 @@ use crate::commands::create_cgroup_manager;
500 pub fn update(args: Update, root_path: PathBuf) -> Result<()> {
501 let cmanager = create_cgroup_manager(root_path, &args.container_id)?;
502
503- let linux_res: LinuxResources;
504- if let Some(resources_path) = args.resources {
505- linux_res = if resources_path.to_string_lossy() == "-" {
506- serde_json::from_reader(io::stdin())?
507+ if let Some(cmanager) = cmanager {
508+ let linux_res: LinuxResources;
509+ if let Some(resources_path) = args.resources {
510+ linux_res = if resources_path.to_string_lossy() == "-" {
511+ serde_json::from_reader(io::stdin())?
512+ } else {
513+ let file = fs::File::open(resources_path)?;
514+ let reader = io::BufReader::new(file);
515+ serde_json::from_reader(reader)?
516+ };
517 } else {
518- let file = fs::File::open(resources_path)?;
519- let reader = io::BufReader::new(file);
520- serde_json::from_reader(reader)?
521- };
522- } else {
523- let mut builder = LinuxResourcesBuilder::default();
524- if let Some(new_pids_limit) = args.pids_limit {
525- builder = builder.pids(LinuxPidsBuilder::default().limit(new_pids_limit).build()?);
526+ let mut builder = LinuxResourcesBuilder::default();
527+ if let Some(new_pids_limit) = args.pids_limit {
528+ builder = builder.pids(LinuxPidsBuilder::default().limit(new_pids_limit).build()?);
529+ }
530+ linux_res = builder.build()?;
531 }
532- linux_res = builder.build()?;
533- }
534
535- cmanager.apply(&ControllerOpt {
536- resources: &linux_res,
537- disable_oom_killer: false,
538- oom_score_adj: None,
539- freezer_state: None,
540- })?;
541+ cmanager.apply(&ControllerOpt {
542+ resources: &linux_res,
543+ disable_oom_killer: false,
544+ oom_score_adj: None,
545+ freezer_state: None,
546+ })?;
547+ }
548 Ok(())
549 }