cgroups_rs/systemd/
props.rs

1// Copyright (c) 2025 Ant Group
2//
3// SPDX-License-Identifier: Apache-2.0 or MIT
4//
5
6use zbus::zvariant::Value as ZbusValue;
7
8use crate::fs::hierarchies;
9use crate::systemd::utils::is_slice_unit;
10use crate::systemd::{
11    BLOCK_IO_ACCOUNTING, CPU_ACCOUNTING, DEFAULT_DEPENDENCIES, DEFAULT_DESCRIPTION, DELEGATE,
12    DESCRIPTION, IO_ACCOUNTING, MEMORY_ACCOUNTING, PIDS, SLICE, TASKS_ACCOUNTING,
13    TIMEOUT_STOP_USEC, WANTS,
14};
15
16pub type Property<'a> = (&'a str, ZbusValue<'a>);
17
18#[derive(Debug, Clone, Default)]
19pub struct PropertiesBuilder {
20    cpu_accounting: Option<bool>,
21    // MemoryAccount is for cgroup v2 as documented in dbus. However,
22    // "github.com/opencontainer/runc" uses it for all. Shall we follow the
23    // same way?
24    memory_accounting: Option<bool>,
25    task_accounting: Option<bool>,
26    // Use IO_ACCOUNTING for cgroup v2 and BLOCK_IO_ACCOUNTING for cgroup v1.
27    io_accounting: Option<bool>,
28    default_dependencies: Option<bool>,
29    description: Option<String>,
30    wants: Option<String>,
31    slice: Option<String>,
32    delegate: Option<bool>,
33    pids: Option<Vec<u32>>,
34    timeout_stop_usec: Option<u64>,
35}
36
37impl PropertiesBuilder {
38    pub fn default_cgroup(slice: &str, unit: &str) -> Self {
39        let mut builder = Self::default()
40            .cpu_accounting(true)
41            .memory_accounting(true)
42            .task_accounting(true)
43            .io_accounting(true)
44            .default_dependencies(false)
45            .description(format!("{} {}:{}", DEFAULT_DESCRIPTION, slice, unit));
46
47        if is_slice_unit(unit) {
48            // If we create a slice, the parent is defined via a Wants=.
49            builder = builder.wants(slice.to_string());
50        } else {
51            // Otherwise it's a scope, which we put into a Slice=.
52            builder = builder.slice(slice.to_string());
53            // Assume scopes always support delegation (supported since systemd v218).
54            builder = builder.delegate(true);
55        }
56
57        builder
58    }
59
60    pub fn cpu_accounting(mut self, enabled: bool) -> Self {
61        self.cpu_accounting = Some(enabled);
62        self
63    }
64
65    pub fn memory_accounting(mut self, enabled: bool) -> Self {
66        self.memory_accounting = Some(enabled);
67        self
68    }
69
70    pub fn task_accounting(mut self, enabled: bool) -> Self {
71        self.task_accounting = Some(enabled);
72        self
73    }
74
75    pub fn io_accounting(mut self, enabled: bool) -> Self {
76        self.io_accounting = Some(enabled);
77        self
78    }
79
80    pub fn default_dependencies(mut self, enabled: bool) -> Self {
81        self.default_dependencies = Some(enabled);
82        self
83    }
84
85    pub fn description(mut self, desc: String) -> Self {
86        self.description = Some(desc);
87        self
88    }
89
90    pub fn wants(mut self, wants: String) -> Self {
91        self.wants = Some(wants);
92        self
93    }
94
95    pub fn slice(mut self, slice: String) -> Self {
96        self.slice = Some(slice);
97        self
98    }
99
100    pub fn delegate(mut self, enabled: bool) -> Self {
101        self.delegate = Some(enabled);
102        self
103    }
104
105    pub fn pids(mut self, pids: Vec<u32>) -> Self {
106        self.pids = Some(pids);
107        self
108    }
109
110    pub fn timeout_stop_usec(mut self, timeout: u64) -> Self {
111        self.timeout_stop_usec = Some(timeout);
112        self
113    }
114
115    pub fn build(self) -> Vec<Property<'static>> {
116        let mut props = vec![];
117
118        if let Some(cpu_accounting) = self.cpu_accounting {
119            props.push((CPU_ACCOUNTING, ZbusValue::Bool(cpu_accounting)));
120        }
121
122        if let Some(memory_accounting) = self.memory_accounting {
123            props.push((MEMORY_ACCOUNTING, ZbusValue::Bool(memory_accounting)));
124        }
125
126        if let Some(task_accounting) = self.task_accounting {
127            props.push((TASKS_ACCOUNTING, ZbusValue::Bool(task_accounting)));
128        }
129
130        if let Some(io_accounting) = self.io_accounting {
131            if hierarchies::is_cgroup2_unified_mode() {
132                props.push((IO_ACCOUNTING, ZbusValue::Bool(io_accounting)));
133            } else {
134                props.push((BLOCK_IO_ACCOUNTING, ZbusValue::Bool(io_accounting)));
135            }
136        }
137
138        if let Some(default_dependencies) = self.default_dependencies {
139            props.push((DEFAULT_DEPENDENCIES, ZbusValue::Bool(default_dependencies)));
140        }
141
142        if let Some(description) = self.description {
143            props.push((DESCRIPTION, ZbusValue::Str(description.into())));
144        } else {
145            props.push((DESCRIPTION, ZbusValue::Str(DEFAULT_DESCRIPTION.into())));
146        }
147
148        if let Some(wants) = self.wants {
149            props.push((WANTS, ZbusValue::Str(wants.into())));
150        }
151
152        if let Some(slice) = self.slice {
153            props.push((SLICE, ZbusValue::Str(slice.into())));
154        }
155
156        if let Some(delegate) = self.delegate {
157            props.push((DELEGATE, ZbusValue::Bool(delegate)));
158        }
159
160        if let Some(pids) = self.pids {
161            props.push((PIDS, ZbusValue::Array(pids.into())));
162        }
163
164        if let Some(timeout) = self.timeout_stop_usec {
165            props.push((TIMEOUT_STOP_USEC, ZbusValue::U64(timeout)));
166        }
167
168        props
169    }
170}