1use std::net::IpAddr;
8use std::time::Duration;
9
10pub use zbus::zvariant::Fd;
11use zbus::zvariant::{Signature, Structure, Type, Value};
12
13pub trait PropValue: Type + Into<Value<'static>> {}
15impl<T: Type + Into<Value<'static>>> PropValue for T {}
16
17macro_rules! unit_properties {
18 ($( $name:ident : $ty:ty = $sig:literal ),* $(,)?) => {
19 #[allow(clippy::upper_case_acronyms)]
21 #[derive(Debug)]
22 pub enum UnitProperty {
23 $( #[doc = concat!("`", $sig, "`")] $name($ty), )*
24 Custom(std::borrow::Cow<'static, str>, zbus::zvariant::Value<'static>),
26 }
27
28 impl UnitProperty {
29 pub fn into_pair(self) -> (std::borrow::Cow<'static, str>, zbus::zvariant::Value<'static>) {
30 match self {
31 $( Self::$name(v) => (std::borrow::Cow::Borrowed(stringify!($name)), v.into()), )*
32 Self::Custom(k, v) => (k, v),
33 }
34 }
35 }
36
37 #[test]
39 fn signatures() {
40 $( assert_eq!(<$ty as zbus::zvariant::Type>::SIGNATURE.to_string(), $sig, stringify!($name)); )*
41 }
42 };
43}
44
45macro_rules! str_enum {
46 ($name:ident { $($var:ident => $s:literal),+ $(,)? }) => {
47 #[allow(non_camel_case_types)]
48 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49 pub enum $name { $($var),+ }
50
51 impl $name {
52 pub const fn as_str(self) -> &'static str {
53 match self { $(Self::$var => $s),+ }
54 }
55 }
56
57 impl std::str::FromStr for $name {
58 type Err = zbus::zvariant::Error;
59 fn from_str(s: &str) -> Result<Self, Self::Err> {
60 match s {
61 $($s => Ok(Self::$var),)+
62 _ => Err(zbus::zvariant::Error::Message(format!(concat!("invalid ", stringify!($name), ": {}"), s))),
63 }
64 }
65 }
66
67 impl zbus::zvariant::Type for $name {
68 const SIGNATURE: &'static zbus::zvariant::Signature = &zbus::zvariant::Signature::Str;
69 }
70
71 impl From<$name> for zbus::zvariant::Value<'_> {
72 fn from(v: $name) -> Self { zbus::zvariant::Value::from(v.as_str()) }
73 }
74
75 impl TryFrom<zbus::zvariant::Value<'_>> for $name {
76 type Error = zbus::zvariant::Error;
77 fn try_from(v: zbus::zvariant::Value<'_>) -> Result<Self, Self::Error> {
78 <&str>::try_from(&v)?.parse()
79 }
80 }
81 };
82}
83
84mod generated;
85pub use generated::*;
86
87pub fn get_property<F, R>(props: &[UnitProperty], filter: F) -> Option<R>
88where
89 F: Fn(&UnitProperty) -> Option<R>,
90{
91 props
92 .iter()
93 .flat_map(|props| filter(props).into_iter())
94 .next()
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub struct Usec(pub u64);
102
103impl Usec {
104 pub const INFINITY: Self = Self(u64::MAX);
105}
106
107impl From<Duration> for Usec {
108 fn from(d: Duration) -> Self {
109 Self(u64::try_from(d.as_micros()).unwrap_or(u64::MAX))
110 }
111}
112
113impl From<Usec> for Duration {
114 fn from(value: Usec) -> Self {
115 Self::from_micros(value.0)
116 }
117}
118
119impl Type for Usec {
120 const SIGNATURE: &'static Signature = &Signature::U64;
121}
122
123impl From<Usec> for Value<'_> {
124 fn from(u: Usec) -> Self {
125 Value::from(u.0)
126 }
127}
128
129#[derive(Debug, Clone, Type, Value)]
132pub struct ExecCommand {
133 pub path: String,
134 pub argv: Vec<String>,
136 pub ignore_failure: bool,
137}
138
139#[derive(Debug, Clone, Type, Value)]
140pub struct ExecCommandEx {
141 pub path: String,
142 pub argv: Vec<String>,
143 pub flags: Vec<ExecCommandFlag>,
144}
145
146#[derive(Debug, Type, Value)]
147pub struct ExtraFd {
148 pub fd: Fd<'static>,
149 pub name: String,
150}
151
152#[derive(Debug, Clone, Type, Value)]
153pub struct Condition {
154 pub kind: String,
156 pub trigger: bool,
158 pub negate: bool,
160 pub parameter: String,
161}
162
163#[derive(Debug, Clone, Default, Type, Value)]
164pub struct ExitStatusSet {
165 pub statuses: Vec<i32>,
166 pub signals: Vec<i32>,
167}
168
169#[derive(Debug, Clone, Type, Value)]
170pub struct OpenFile {
171 pub path: String,
172 pub fd_name: String,
173 pub flags: u64,
175}
176
177#[derive(Debug, Clone, Type, Value)]
178pub struct RefreshOnReload {
179 pub invert: bool,
181 pub flag: String,
183}
184
185#[derive(Debug, Clone, Type, Value)]
186pub struct SecurityLabel {
187 pub ignore_failure: bool,
189 pub label: String,
190}
191
192pub const MS_REC: u64 = 16384;
193
194#[derive(Debug, Clone, Type, Value)]
195pub struct BindPath {
196 pub source: String,
197 pub destination: String,
198 pub ignore_missing: bool,
200 pub flags: u64,
202}
203
204#[derive(Debug, Clone, Copy)]
206pub struct DirectoryQuota {
207 pub absolute: u64,
209 pub scale: u32,
211 pub enforce: bool,
212}
213
214impl Type for DirectoryQuota {
215 const SIGNATURE: &'static Signature = <(u64, u32, String)>::SIGNATURE;
216}
217
218impl From<DirectoryQuota> for Value<'_> {
219 fn from(q: DirectoryQuota) -> Self {
220 Structure::from((q.absolute, q.scale, if q.enforce { "yes" } else { "no" })).into()
221 }
222}
223
224#[derive(Debug, Clone, Type, Value)]
225pub struct DirectorySymlink {
226 pub source: String,
227 pub destination: String,
228 pub flags: u64,
230}
231
232#[derive(Debug, Clone, Type, Value)]
233pub struct EnvironmentFile {
234 pub path: String,
235 pub ignore_missing: bool,
236}
237
238#[derive(Debug, Clone, Type, Value)]
239pub struct MountOption {
240 pub partition: String,
242 pub options: String,
243}
244
245#[derive(Debug, Clone, Type, Value)]
246pub struct ExtensionImage {
247 pub source: String,
248 pub ignore_missing: bool,
249 pub options: Vec<MountOption>,
250}
251
252#[derive(Debug, Clone, Type, Value)]
253pub struct MountImage {
254 pub source: String,
255 pub destination: String,
256 pub ignore_missing: bool,
257 pub options: Vec<MountOption>,
258}
259
260#[derive(Debug, Clone, Type, Value)]
261pub struct ImportCredential {
262 pub glob: String,
263 pub rename: String,
265}
266
267#[derive(Debug, Clone, Type, Value)]
268pub struct LoadCredential {
269 pub id: String,
270 pub path: String,
271}
272
273#[derive(Debug, Clone, Type, Value)]
274pub struct SetCredential {
275 pub id: String,
276 pub data: Vec<u8>,
277}
278
279#[derive(Debug, Clone, Type, Value)]
280pub struct LogFilterPattern {
281 pub allow: bool,
283 pub pattern: String,
284}
285
286#[derive(Debug, Clone, Type, Value)]
287pub struct ProtectHostnameEx {
288 pub mode: ProtectHostname,
289 pub hostname: String,
291}
292
293#[derive(Debug, Clone, Type, Value)]
296pub struct FilterList {
297 pub allow_list: bool,
299 pub items: Vec<String>,
300}
301
302#[derive(Debug, Clone, Type, Value)]
303pub struct TmpFs {
304 pub path: String,
305 pub options: String,
306}
307
308#[derive(Debug, Clone, Type, Value)]
309pub struct BpfProgram {
310 pub attach_type: String,
312 pub path: String,
313}
314
315#[derive(Debug, Clone, Type, Value)]
316pub struct DeviceAllow {
317 pub path: String,
318 pub access: String,
320}
321
322#[derive(Debug, Clone, Type, Value)]
324pub struct DeviceValue {
325 pub path: String,
326 pub value: u64,
327}
328
329#[derive(Debug, Clone, Copy, Type, Value)]
330pub struct SocketBind {
331 pub family: i32,
333 pub protocol: i32,
335 pub port_count: u16,
337 pub port_min: u16,
338}
339
340#[derive(Debug, Clone, Type, Value)]
341pub struct NftSet {
342 pub source: i32,
344 pub nfproto: i32,
346 pub table: String,
347 pub set: String,
348}
349
350#[derive(Debug, Clone, Copy)]
352pub struct IpPrefix {
353 pub address: IpAddr,
354 pub prefix_len: u32,
355}
356
357impl Type for IpPrefix {
358 const SIGNATURE: &'static Signature = <(i32, Vec<u8>, u32)>::SIGNATURE;
359}
360
361impl From<IpPrefix> for Value<'_> {
362 fn from(p: IpPrefix) -> Self {
363 const AF_INET: i32 = 2;
364 const AF_INET6: i32 = 10;
365 let (af, bytes) = match p.address {
366 IpAddr::V4(a) => (AF_INET, a.octets().to_vec()),
367 IpAddr::V6(a) => (AF_INET6, a.octets().to_vec()),
368 };
369 Structure::from((af, bytes, p.prefix_len)).into()
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376
377 #[test]
378 fn exec_start_wire_format() {
379 let (k, v) = UnitProperty::ExecStart(vec![ExecCommand {
380 path: "/bin/true".into(),
381 argv: vec!["true".into()],
382 ignore_failure: false,
383 }])
384 .into_pair();
385 assert_eq!(k, "ExecStart");
386 assert_eq!(v.value_signature().to_string(), "a(sasb)");
387 }
388
389 #[test]
390 fn enum_round_trip() {
391 let v: Value = ServiceType::NotifyReload.into();
392 assert_eq!(ServiceType::try_from(v).unwrap(), ServiceType::NotifyReload);
393 }
394}