1 // Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved.
2 //
3 // StratoVirt is licensed under Mulan PSL v2.
4 // You can use this software according to the terms and conditions of the Mulan
5 // PSL v2.
6 // You may obtain a copy of Mulan PSL v2 at:
7 //         http://license.coscl.org.cn/MulanPSL2
8 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
9 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
10 // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
11 // See the Mulan PSL v2 for more details.
12 
13 mod commands;
14 mod container;
15 mod linux;
16 mod utils;
17 
18 use std::{
19     fs::remove_dir_all,
20     path::{Path, PathBuf},
21     process::exit,
22 };
23 
24 use anyhow::{anyhow, Context, Result};
25 use clap::{crate_description, Args, Parser, Subcommand};
26 use commands::{Delete, Exec, Kill, Start, State};
27 use log::info;
28 use nix::unistd::geteuid;
29 
30 use crate::{commands::Create, utils::logger};
31 
32 // Global options which are not binded to any specific command.
33 #[derive(Args, Debug)]
34 struct GlobalOpts {
35     /// Root directory to store container state.
36     #[arg(short, long)]
37     root: Option<PathBuf>,
38     /// Path of log file.
39     #[arg(short, long)]
40     log: Option<PathBuf>,
41     /// Enable debug log level.
42     #[arg(short, long)]
43     debug: bool,
44 }
45 
46 // Standard commands supported by [OCI runtime-spec]
47 // (https://github.com/opencontainers/runtime-spec/blob/master/runtime.md)
48 // and [OCI Command Line Interface]
49 // (https://github.com/opencontainers/runtime-tools/blob/master/docs/command-line-interface.md).
50 #[derive(Subcommand, Debug)]
51 enum StandardCmd {
52     Create(Create),
53     Start(Start),
54     State(State),
55     Kill(Kill),
56     Delete(Delete),
57 }
58 
59 // Extended commands not documented in [OCI Command Line Interface].
60 #[derive(Subcommand, Debug)]
61 enum ExtendCmd {
62     Exec(Exec),
63 }
64 
65 #[derive(Subcommand, Debug)]
66 enum Command {
67     #[command(flatten)]
68     Standard(StandardCmd),
69     #[command(flatten)]
70     Extend(ExtendCmd),
71 }
72 
73 #[derive(Parser, Debug)]
74 #[command(version, author, about = crate_description!())]
75 #[command(propagate_version = true)]
76 struct Cli {
77     #[command(flatten)]
78     global: GlobalOpts,
79     #[command(subcommand)]
80     cmd: Command,
81 }
82 
cmd_run(command: Command, root: &Path) -> Result<()>83 fn cmd_run(command: Command, root: &Path) -> Result<()> {
84     match command {
85         Command::Standard(cmd) => match cmd {
86             StandardCmd::Create(create) => {
87                 info!("Run command: {:?}", create);
88 
89                 let mut root_exist = false;
90                 create.run(root, &mut root_exist).map_err(|e| {
91                     if !root_exist {
92                         let _ = remove_dir_all(root);
93                     }
94                     anyhow!(e)
95                 })?
96             }
97             StandardCmd::Start(start) => {
98                 info!("Run command: {:?}", start);
99                 start.run(root)?
100             }
101             StandardCmd::Kill(kill) => {
102                 info!("Run command: {:?}", kill);
103                 kill.run(root)?
104             }
105             StandardCmd::Delete(delete) => {
106                 info!("Run command: {:?}", delete);
107                 delete.run(root)?
108             }
109             StandardCmd::State(state) => {
110                 info!("Run command: {:?}", state);
111                 state.run(root)?
112             }
113         },
114         Command::Extend(cmd) => match cmd {
115             ExtendCmd::Exec(exec) => {
116                 info!("Run command: {:?}", exec);
117                 exec.run(root)?
118             }
119         },
120     }
121     Ok(())
122 }
123 
real_main() -> Result<()>124 fn real_main() -> Result<()> {
125     let cli = Cli::parse();
126 
127     logger::init(&cli.global.log, cli.global.debug).with_context(|| "Failed to init logger")?;
128 
129     let root_path = if let Some(root) = cli.global.root {
130         root
131     } else {
132         let euid = geteuid();
133         PathBuf::from(format!("/var/run/user/{}/ozonec", euid))
134     };
135     cmd_run(cli.cmd, &root_path)
136 }
137 
main()138 fn main() {
139     if let Err(e) = real_main() {
140         eprintln!("ERROR: {:?}", e);
141         exit(1);
142     }
143     exit(0);
144 }
145