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