1use std::error::Error;
2use std::path::PathBuf;
3
4use clap::Args;
5
6#[derive(Args, Debug)]
9pub struct Exec {
10 #[arg(long)]
12 pub console_socket: Option<PathBuf>,
13 #[arg(long)]
14 pub cwd: Option<PathBuf>,
16 #[arg(short, long, value_parser = parse_env::<String, String>, num_args = 1)]
18 pub env: Vec<(String, String)>,
19 #[arg(short, long)]
21 pub tty: bool,
22 #[arg(short, long, value_parser = parse_user::<u32, u32>)]
24 pub user: Option<(u32, Option<u32>)>,
25 #[arg(long, short = 'g', num_args = 1)]
27 pub additional_gids: Vec<u32>,
28 #[arg(short, long)]
30 pub process: Option<PathBuf>,
31 #[arg(short, long)]
33 pub detach: bool,
34 #[arg(long)]
35 pub pid_file: Option<PathBuf>,
37 #[arg(long)]
39 pub process_label: Option<String>,
40 #[arg(long)]
42 pub apparmor: Option<String>,
43 #[arg(long)]
45 pub no_new_privs: bool,
46 #[arg(long, num_args = 1)]
48 pub cap: Vec<String>,
49 #[arg(long, default_value = "0")]
51 pub preserve_fds: i32,
52 #[arg(long)]
54 pub ignore_paused: bool,
55 #[arg(long)]
57 pub cgroup: Option<String>,
58 #[arg(value_parser = clap::builder::NonEmptyStringValueParser::new(), required = true)]
60 pub container_id: String,
61 #[arg(required = false, trailing_var_arg = true)]
63 pub command: Vec<String>,
64}
65
66fn parse_env<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
67where
68 T: std::str::FromStr,
69 T::Err: Error + Send + Sync + 'static,
70 U: std::str::FromStr,
71 U::Err: Error + Send + Sync + 'static,
72{
73 let pos = s
74 .find('=')
75 .ok_or_else(|| format!("invalid VAR=value: no `=` found in `{s}`"))?;
76 Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
77}
78
79fn parse_user<T, U>(s: &str) -> Result<(T, Option<U>), Box<dyn Error + Send + Sync + 'static>>
80where
81 T: std::str::FromStr,
82 T::Err: Error + Send + Sync + 'static,
83 U: std::str::FromStr,
84 U::Err: Error + Send + Sync + 'static,
85{
86 if let Some(pos) = s.find(':') {
87 Ok((s[..pos].parse()?, Some(s[pos + 1..].parse()?)))
88 } else {
89 Ok((s.parse()?, None))
90 }
91}