1 // Copyright (c) 2020 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 use std::io::Write;
14 use std::process::{ExitCode, Termination};
15 use std::sync::{Arc, Mutex};
16 
17 use anyhow::{bail, Context, Result};
18 use log::{error, info};
19 use thiserror::Error;
20 
21 use machine::{type_init, LightMachine, MachineOps, StdMachine};
22 use machine_manager::{
23     cmdline::{check_api_channel, create_args_parser, create_vmconfig},
24     config::MachineType,
25     config::VmConfig,
26     event_loop::EventLoop,
27     qmp::qmp_channel::QmpChannel,
28     qmp::qmp_socket::Socket,
29     signal_handler::{exit_with_code, handle_signal, register_kill_signal, VM_EXIT_GENE_ERR},
30     temp_cleaner::TempCleaner,
31     test_server::TestSock,
32 };
33 use util::loop_context::EventNotifierHelper;
34 use util::test_helper::{is_test_enabled, set_test_enabled};
35 use util::{arg_parser, daemonize::daemonize, logger, set_termi_canon_mode};
36 
37 #[derive(Error, Debug)]
38 enum MainError {
39     #[error("Manager")]
40     Manager {
41         #[from]
42         source: machine_manager::error::MachineManagerError,
43     },
44     #[error("Util")]
45     Util {
46         #[from]
47         source: util::error::UtilError,
48     },
49     #[error("Machine")]
50     Machine {
51         #[from]
52         source: machine::error::MachineError,
53     },
54     #[error("Io")]
55     Io {
56         #[from]
57         source: std::io::Error,
58     },
59 }
60 
main() -> ExitCode61 fn main() -> ExitCode {
62     match run() {
63         Ok(ret) => ret.report(),
64         Err(ref e) => {
65             write!(&mut std::io::stderr(), "{}", format_args!("{:?}\r\n", e))
66                 .expect("Error writing to stderr");
67 
68             ExitCode::FAILURE
69         }
70     }
71 }
72 
run() -> Result<()>73 fn run() -> Result<()> {
74     type_init()?;
75 
76     let cmd_args = create_args_parser().get_matches()?;
77 
78     if cmd_args.is_present("mod-test") {
79         let machine = cmd_args.value_of("machine").unwrap_or_default();
80         let accel = cmd_args.value_of("accel").unwrap_or_default();
81         if !machine.contains("accel=test") && accel.ne("test") {
82             bail!("MST can only use test accel!");
83         }
84         set_test_enabled();
85     }
86 
87     let logfile_path = cmd_args.value_of("display log").unwrap_or_default();
88     logger::init_log(logfile_path)?;
89 
90     std::panic::set_hook(Box::new(|panic_msg| {
91         set_termi_canon_mode().expect("Failed to set terminal to canonical mode.");
92 
93         let panic_file = panic_msg.location().map_or("", |loc| loc.file());
94         let panic_line = panic_msg.location().map_or(0, |loc| loc.line());
95         if let Some(msg) = panic_msg.payload().downcast_ref::<&str>() {
96             error!("Panic at [{}: {}]: {}.", panic_file, panic_line, msg);
97         } else {
98             error!("Panic at [{}: {}].", panic_file, panic_line);
99         }
100 
101         // clean temporary file
102         TempCleaner::clean();
103         exit_with_code(VM_EXIT_GENE_ERR);
104     }));
105 
106     let mut vm_config: VmConfig = match create_vmconfig(&cmd_args) {
107         Ok(vm_cfg) => vm_cfg,
108         Err(e) => {
109             error!("Failed to create vmconfig {:?}", e);
110             return Err(e);
111         }
112     };
113     info!("VmConfig is {:?}", vm_config);
114 
115     match real_main(&cmd_args, &mut vm_config) {
116         Ok(()) => {
117             info!("MainLoop over, Vm exit");
118             // clean temporary file
119             TempCleaner::clean();
120             EventLoop::loop_clean();
121             handle_signal();
122         }
123         Err(ref e) => {
124             set_termi_canon_mode().expect("Failed to set terminal to canonical mode.");
125             error!("{}", format!("{:?}\r\n", e));
126             // clean temporary file
127             TempCleaner::clean();
128             EventLoop::loop_clean();
129             exit_with_code(VM_EXIT_GENE_ERR);
130         }
131     }
132 
133     Ok(())
134 }
135 
real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Result<()>136 fn real_main(cmd_args: &arg_parser::ArgMatches, vm_config: &mut VmConfig) -> Result<()> {
137     TempCleaner::object_init();
138 
139     if cmd_args.is_present("daemonize") {
140         match daemonize(cmd_args.value_of("pidfile")) {
141             Ok(()) => {
142                 if let Some(pidfile) = cmd_args.value_of("pidfile") {
143                     TempCleaner::add_path(pidfile);
144                 }
145                 info!("Daemonize mode start!");
146             }
147             Err(e) => bail!("Daemonize start failed: {}", e),
148         }
149     } else if cmd_args.value_of("pidfile").is_some() {
150         bail!("-pidfile must be used with -daemonize together.");
151     }
152 
153     QmpChannel::object_init();
154     EventLoop::object_init(&vm_config.iothreads)?;
155     register_kill_signal();
156 
157     let listeners = check_api_channel(cmd_args, vm_config)?;
158     let mut sockets = Vec::new();
159     let vm: Arc<Mutex<dyn MachineOps + Send + Sync>> = match vm_config.machine_config.mach_type {
160         MachineType::MicroVm => {
161             if is_test_enabled() {
162                 panic!("module test framework does not support microvm.")
163             }
164             let vm = Arc::new(Mutex::new(
165                 LightMachine::new(vm_config).with_context(|| "Failed to init MicroVM")?,
166             ));
167             MachineOps::realize(&vm, vm_config).with_context(|| "Failed to realize micro VM.")?;
168             EventLoop::set_manager(vm.clone());
169 
170             for listener in listeners {
171                 sockets.push(Socket::from_listener(listener, Some(vm.clone())));
172             }
173             vm
174         }
175         MachineType::StandardVm => {
176             let vm = Arc::new(Mutex::new(
177                 StdMachine::new(vm_config).with_context(|| "Failed to init StandardVM")?,
178             ));
179             MachineOps::realize(&vm, vm_config)
180                 .with_context(|| "Failed to realize standard VM.")?;
181             EventLoop::set_manager(vm.clone());
182 
183             if is_test_enabled() {
184                 let sock_path = cmd_args.value_of("mod-test");
185                 let test_sock = TestSock::new(sock_path.unwrap().as_str(), vm.clone());
186                 EventLoop::update_event(
187                     EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(test_sock))),
188                     None,
189                 )
190                 .with_context(|| "Failed to add test socket to MainLoop")?;
191             }
192 
193             for listener in listeners {
194                 sockets.push(Socket::from_listener(listener, Some(vm.clone())));
195             }
196             vm
197         }
198         MachineType::None => {
199             if is_test_enabled() {
200                 panic!("please specify machine type.")
201             }
202             let vm = Arc::new(Mutex::new(
203                 StdMachine::new(vm_config).with_context(|| "Failed to init NoneVM")?,
204             ));
205             EventLoop::set_manager(vm.clone());
206 
207             for listener in listeners {
208                 sockets.push(Socket::from_listener(listener, Some(vm.clone())));
209             }
210             vm
211         }
212     };
213 
214     let balloon_switch_on = vm_config.dev_name.contains_key("balloon");
215     if !cmd_args.is_present("disable-seccomp") {
216         vm.lock()
217             .unwrap()
218             .register_seccomp(balloon_switch_on)
219             .with_context(|| "Failed to register seccomp rules.")?;
220     }
221 
222     for socket in sockets {
223         EventLoop::update_event(
224             EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(socket))),
225             None,
226         )
227         .with_context(|| "Failed to add api event to MainLoop")?;
228     }
229 
230     machine::vm_run(&vm, cmd_args).with_context(|| "Failed to start VM.")?;
231 
232     EventLoop::loop_run().with_context(|| "MainLoop exits unexpectedly: error occurs")?;
233     Ok(())
234 }
235