in src/firecracker/src/main.rs [68:373]
fn main_exitable() -> ExitCode {
LOGGER
.configure(Some(DEFAULT_INSTANCE_ID.to_string()))
.expect("Failed to register logger");
if let Err(e) = register_signal_handlers() {
error!("Failed to register signal handlers: {}", e);
return vmm::FC_EXIT_CODE_GENERIC_ERROR;
}
#[cfg(target_arch = "aarch64")]
enable_ssbd_mitigation();
// We need this so that we can reset terminal to canonical mode if panic occurs.
let stdin = io::stdin();
// Start firecracker by setting up a panic hook, which will be called before
// terminating as we're building with panic = "abort".
// It's worth noting that the abort is caused by sending a SIG_ABORT signal to the process.
panic::set_hook(Box::new(move |info| {
// We're currently using the closure parameter, which is a &PanicInfo, for printing the
// origin of the panic, including the payload passed to panic! and the source code location
// from which the panic originated.
error!("Firecracker {}", info);
if let Err(e) = stdin.lock().set_canon_mode() {
error!(
"Failure while trying to reset stdin to canonical mode: {}",
e
);
}
METRICS.vmm.panic_count.inc();
// Write the metrics before aborting.
if let Err(e) = METRICS.write() {
error!("Failed to write metrics while panicking: {}", e);
}
}));
let mut arg_parser = ArgParser::new()
.arg(
Argument::new("api-sock")
.takes_value(true)
.default_value(DEFAULT_API_SOCK_PATH)
.help("Path to unix domain socket used by the API."),
)
.arg(
Argument::new("id")
.takes_value(true)
.default_value(DEFAULT_INSTANCE_ID)
.help("MicroVM unique identifier."),
)
.arg(
Argument::new("seccomp-filter")
.takes_value(true)
.forbids(vec!["no-seccomp"])
.help(
"Optional parameter which allows specifying the path to a custom seccomp filter. For advanced users."
),
)
.arg(
Argument::new("no-seccomp")
.takes_value(false)
.forbids(vec!["seccomp-filter"])
.help("Optional parameter which allows starting and using a microVM without seccomp filtering. \
Not recommended.")
)
.arg(
Argument::new("start-time-us")
.takes_value(true)
.help("Process start time (wall clock, microseconds). This parameter is optional."),
)
.arg(
Argument::new("start-time-cpu-us")
.takes_value(true)
.help("Process start CPU time (wall clock, microseconds). This parameter is optional."),
)
.arg(
Argument::new("parent-cpu-time-us")
.takes_value(true)
.help("Parent process CPU time (wall clock, microseconds). This parameter is optional."),
)
.arg(
Argument::new("config-file")
.takes_value(true)
.help("Path to a file that contains the microVM configuration in JSON format."),
)
.arg(
Argument::new(MMDS_CONTENT_ARG)
.takes_value(true)
.help("Path to a file that contains metadata in JSON format to add to the mmds.")
)
.arg(
Argument::new("no-api")
.takes_value(false)
.requires("config-file")
.help("Optional parameter which allows starting and using a microVM without an active API socket.")
)
.arg(
Argument::new("log-path")
.takes_value(true)
.help("Path to a fifo or a file used for configuring the logger on startup.")
)
.arg(
Argument::new("level")
.takes_value(true)
.requires("log-path")
.default_value("Warning")
.help("Set the logger level.")
)
.arg(
Argument::new("show-level")
.takes_value(false)
.requires("log-path")
.help("Whether or not to output the level in the logs.")
)
.arg(
Argument::new("show-log-origin")
.takes_value(false)
.requires("log-path")
.help("Whether or not to include the file path and line number of the log's origin.")
)
.arg(
Argument::new("boot-timer")
.takes_value(false)
.help("Whether or not to load boot timer device for logging elapsed time since InstanceStart command.")
)
.arg(
Argument::new("version")
.takes_value(false)
.help("Print the binary version number and a list of supported snapshot data format versions.")
)
.arg(
Argument::new("describe-snapshot")
.takes_value(true)
.help("Print the data format version of the provided snapshot state file.")
)
.arg(
Argument::new("http-api-max-payload-size")
.takes_value(true)
.help("Http API request payload max size.")
);
let arguments = match arg_parser.parse_from_cmdline() {
Err(err) => {
error!(
"Arguments parsing error: {} \n\n\
For more information try --help.",
err
);
return vmm::FC_EXIT_CODE_ARG_PARSING;
}
_ => {
if arg_parser.arguments().flag_present("help") {
println!("Firecracker v{}\n", FIRECRACKER_VERSION);
println!("{}", arg_parser.formatted_help());
return vmm::FC_EXIT_CODE_OK;
}
if arg_parser.arguments().flag_present("version") {
println!("Firecracker v{}\n", FIRECRACKER_VERSION);
print_supported_snapshot_versions();
return vmm::FC_EXIT_CODE_OK;
}
if let Some(snapshot_path) = arg_parser.arguments().single_value("describe-snapshot") {
print_snapshot_data_format(snapshot_path);
return vmm::FC_EXIT_CODE_OK;
}
arg_parser.arguments()
}
};
// Display warnings for any used deprecated parameters.
// Currently unused since there are no deprecated parameters. Uncomment the line when
// deprecating one.
// warn_deprecated_parameters(&arguments);
// It's safe to unwrap here because the field's been provided with a default value.
let instance_id = arguments.single_value("id").unwrap();
validate_instance_id(instance_id.as_str()).expect("Invalid instance ID");
let instance_info = InstanceInfo {
id: instance_id.clone(),
state: VmState::NotStarted,
vmm_version: FIRECRACKER_VERSION.to_string(),
app_name: "Firecracker".to_string(),
};
LOGGER.set_instance_id(instance_id.to_owned());
if let Some(log) = arguments.single_value("log-path") {
// It's safe to unwrap here because the field's been provided with a default value.
let level = arguments.single_value("level").unwrap().to_owned();
let logger_level = match LoggerLevel::from_string(level) {
Ok(level) => level,
Err(e) => {
return generic_error_exit(&format!(
"Invalid value for logger level: {}.\
Possible values: [Error, Warning, Info, Debug]",
e
));
}
};
let show_level = arguments.flag_present("show-level");
let show_log_origin = arguments.flag_present("show-log-origin");
let logger_config = LoggerConfig::new(
PathBuf::from(log),
logger_level,
show_level,
show_log_origin,
);
if let Err(e) = init_logger(logger_config, &instance_info) {
return generic_error_exit(&format!("Could not initialize logger:: {}", e));
};
}
let mut seccomp_filters: BpfThreadMap = match SeccompConfig::from_args(
arguments.flag_present("no-seccomp"),
arguments.single_value("seccomp-filter"),
)
.and_then(get_filters)
{
Ok(filters) => filters,
Err(e) => {
return generic_error_exit(&format!("Seccomp error: {}", e));
}
};
let vmm_config_json = arguments
.single_value("config-file")
.map(fs::read_to_string)
.map(|x| x.expect("Unable to open or read from the configuration file"));
let metadata_json = arguments
.single_value(MMDS_CONTENT_ARG)
.map(fs::read_to_string)
.map(|x| x.expect("Unable to open or read from the mmds content file"));
if let Some(data) = metadata_json {
MMDS.lock()
.expect("Failed to acquire lock on MMDS")
.put_data(
serde_json::from_str(&data).expect("MMDS error: metadata provided not valid json"),
);
info!("Successfully added metadata to mmds from file");
}
let boot_timer_enabled = arguments.flag_present("boot-timer");
let api_enabled = !arguments.flag_present("no-api");
if api_enabled {
let bind_path = arguments
.single_value("api-sock")
.map(PathBuf::from)
.expect("Missing argument: api-sock");
let payload_limit = arg_parser
.arguments()
.single_value("http-api-max-payload-size")
.map(|lim| {
lim.parse::<usize>()
.expect("'http-api-max-payload-size' parameter expected to be of 'usize' type.")
});
let start_time_us = arguments.single_value("start-time-us").map(|s| {
s.parse::<u64>()
.expect("'start-time-us' parameter expected to be of 'u64' type.")
});
let start_time_cpu_us = arguments.single_value("start-time-cpu-us").map(|s| {
s.parse::<u64>()
.expect("'start-time-cpu-us' parameter expected to be of 'u64' type.")
});
let parent_cpu_time_us = arguments.single_value("parent-cpu-time-us").map(|s| {
s.parse::<u64>()
.expect("'parent-cpu-time-us' parameter expected to be of 'u64' type.")
});
let process_time_reporter =
ProcessTimeReporter::new(start_time_us, start_time_cpu_us, parent_cpu_time_us);
api_server_adapter::run_with_api(
&mut seccomp_filters,
vmm_config_json,
bind_path,
instance_info,
process_time_reporter,
boot_timer_enabled,
payload_limit,
)
} else {
let seccomp_filters: BpfThreadMap = seccomp_filters
.into_iter()
.filter(|(k, _)| k != "api")
.collect();
run_without_api(
&seccomp_filters,
vmm_config_json,
instance_info,
boot_timer_enabled,
)
}
}