liboci_cli/
exec.rs

1use std::error::Error;
2use std::path::PathBuf;
3
4use clap::Parser;
5
6/// Execute a process within an existing container
7/// Reference: https://github.com/opencontainers/runc/blob/main/man/runc-exec.8.md
8#[derive(Parser, Debug)]
9pub struct Exec {
10    /// Unix socket (file) path , which will receive file descriptor of the writing end of the pseudoterminal
11    #[clap(long)]
12    pub console_socket: Option<PathBuf>,
13    #[clap(long)]
14    /// Current working directory of the container
15    pub cwd: Option<PathBuf>,
16    /// Environment variables that should be set in the container
17    #[clap(short, long, value_parser = parse_env::<String, String>, number_of_values = 1)]
18    pub env: Vec<(String, String)>,
19    #[clap(short, long)]
20    pub tty: bool,
21    /// Run the command as a user
22    #[clap(short, long, value_parser = parse_user::<u32, u32>)]
23    pub user: Option<(u32, Option<u32>)>,
24    /// Add additional group IDs. Can be specified multiple times
25    #[clap(long, short = 'g', number_of_values = 1)]
26    pub additional_gids: Vec<u32>,
27    /// Path to process.json
28    #[clap(short, long)]
29    pub process: Option<PathBuf>,
30    /// Detach from the container process
31    #[clap(short, long)]
32    pub detach: bool,
33    #[clap(long)]
34    /// The file to which the pid of the container process should be written to
35    pub pid_file: Option<PathBuf>,
36    /// Set the asm process label for the process commonly used with selinux
37    #[clap(long)]
38    pub process_label: Option<String>,
39    /// Set the apparmor profile for the process
40    #[clap(long)]
41    pub apparmor: Option<String>,
42    /// Prevent the process from gaining additional privileges
43    #[clap(long)]
44    pub no_new_privs: bool,
45    /// Add a capability to the bounding set for the process
46    #[clap(long, number_of_values = 1)]
47    pub cap: Vec<String>,
48    /// Pass N additional file descriptors to the container
49    #[clap(long, default_value = "0")]
50    pub preserve_fds: i32,
51    /// Allow exec in a paused container
52    #[clap(long)]
53    pub ignore_paused: bool,
54    /// Execute a process in a sub-cgroup
55    #[clap(long)]
56    pub cgroup: Option<String>,
57
58    /// Identifier of the container
59    #[clap(value_parser = clap::builder::NonEmptyStringValueParser::new(), required = true)]
60    pub container_id: String,
61
62    /// Command that should be executed in the container
63    #[clap(required = false, trailing_var_arg = true)]
64    pub command: Vec<String>,
65}
66
67fn parse_env<T, U>(s: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
68where
69    T: std::str::FromStr,
70    T::Err: Error + Send + Sync + 'static,
71    U: std::str::FromStr,
72    U::Err: Error + Send + Sync + 'static,
73{
74    let pos = s
75        .find('=')
76        .ok_or_else(|| format!("invalid VAR=value: no `=` found in `{s}`"))?;
77    Ok((s[..pos].parse()?, s[pos + 1..].parse()?))
78}
79
80fn parse_user<T, U>(s: &str) -> Result<(T, Option<U>), Box<dyn Error + Send + Sync + 'static>>
81where
82    T: std::str::FromStr,
83    T::Err: Error + Send + Sync + 'static,
84    U: std::str::FromStr,
85    U::Err: Error + Send + Sync + 'static,
86{
87    if let Some(pos) = s.find(':') {
88        Ok((s[..pos].parse()?, Some(s[pos + 1..].parse()?)))
89    } else {
90        Ok((s.parse()?, None))
91    }
92}