blob: 30fd8f1b190624d2283b6893f6f9a10916e2c64b [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Coordinates suspend-to-disk activities
use getopts::{self, Options};
use hiberman::{self, HibernateOptions, ResumeOptions};
use log::error;
fn print_usage(message: &str, error: bool) {
if error {
eprintln!("{}", message)
} else {
println!("{}", message);
}
}
fn init_logging() -> std::result::Result<(), ()> {
if let Err(e) = hiberman::hiberlog::init() {
eprintln!("failed to initialize hiberlog: {}", e);
return Err(());
}
Ok(())
}
fn cookie_usage(error: bool, options: &Options) {
let brief = r#"Usage: hiberman cookie <path> [options]
Get or set the hibernate cookie info. With no options, gets the
current status of the hibernate cookie. Returns 0 if the cookie
indicates a valid hibernate image, or 1 if no image.
"#;
print_usage(&options.usage(brief), error);
}
fn hiberman_cookie(args: &mut std::env::Args) -> std::result::Result<(), ()> {
// Note: Don't fire up logging immediately in this command as it's called
// during very early init, before syslog is ready.
let mut opts = Options::new();
opts.optflag(
"c",
"clear",
"Clear the cookie to indicate no valid hibernate image",
);
opts.optflag("h", "help", "Print this help text");
opts.optflag(
"s",
"set",
"Set the cookie to indicate a valid hibernate image",
);
opts.optflag("v", "verbose", "Print more during the command");
let args: Vec<String> = args.collect();
let matches = match opts.parse(args) {
Ok(m) => m,
Err(e) => {
eprintln!("Failed to parse arguments: {}", e);
cookie_usage(true, &opts);
return Err(());
}
};
if matches.opt_present("h") {
cookie_usage(false, &opts);
return Ok(());
}
let clear_cookie = matches.opt_present("c");
let set_cookie = matches.opt_present("s");
let verbose = matches.opt_present("v");
let path = matches.free.get(0).cloned();
// In verbose mode, or for anything other than "get", fire up logging.
if verbose || set_cookie || clear_cookie {
init_logging()?;
}
if set_cookie || clear_cookie {
if let Err(e) = hiberman::cookie::set_hibernate_cookie(path.as_ref(), set_cookie) {
error!("Failed to write hibernate cookie: {}", e);
return Err(());
}
} else {
let is_set = match hiberman::cookie::get_hibernate_cookie(path.as_ref()) {
Ok(s) => s,
Err(e) => {
error!("Failed to get hibernate cookie: {}", e);
return Err(());
}
};
if verbose {
if is_set {
println!("Hibernate cookie is set");
} else {
println!("Hibernate cookie is not set");
}
}
if !is_set {
return Err(());
}
}
Ok(())
}
fn cat_usage(error: bool, options: &Options) {
let brief = r#"Usage: hiberman cat [options] <file> [file...]
Print a disk file to stdout. Since disk files write to blocks
underneath the file system, they cannot be read reliably by normal
file system accesses.
"#;
print_usage(&options.usage(brief), error);
}
fn hiberman_cat(args: &mut std::env::Args) -> std::result::Result<(), ()> {
init_logging()?;
let mut opts = Options::new();
opts.optflag("l", "log", "Treat the file(s) as log files");
opts.optflag("h", "help", "Print this help text");
let args: Vec<String> = args.collect();
let matches = match opts.parse(args) {
Ok(m) => m,
Err(e) => {
error!("Failed to parse arguments: {}", e);
cat_usage(true, &opts);
return Err(());
}
};
if matches.opt_present("h") {
cat_usage(false, &opts);
return Ok(());
}
let mut result = Ok(());
let is_log = matches.opt_present("l");
for f in matches.free {
if let Err(e) = hiberman::cat::cat_disk_file(&f, is_log) {
error!("Failed to cat {}: {}", &f, e);
result = Err(())
}
}
result
}
fn hibernate_usage(error: bool, options: &Options) {
let brief = r#"Usage: hiberman hibernate [options]
Hibernate the system now.
"#;
print_usage(&options.usage(brief), error);
}
fn hiberman_hibernate(args: &mut std::env::Args) -> std::result::Result<(), ()> {
init_logging()?;
let mut opts = Options::new();
opts.optflag("h", "help", "Print this help text");
opts.optflag("n", "dry-run", "Create the hibernate image, but then exit rather than shutting down. This image should only be restored with --dry-run");
opts.optflag(
"p",
"platform-mode",
"Force enable the use of suspending to platform mode (S4)",
);
opts.optflag(
"u",
"unencrypted",
"Do not encrypt the hibernate image. Use only for test and debugging",
);
opts.optflag("t", "test-keys", "Use test keys for debugging");
let args: Vec<String> = args.collect();
let matches = match opts.parse(args) {
Ok(m) => m,
Err(e) => {
error!("Failed to parse arguments: {}", e);
hibernate_usage(true, &opts);
return Err(());
}
};
if matches.opt_present("h") {
hibernate_usage(false, &opts);
return Ok(());
}
let options = HibernateOptions {
dry_run: matches.opt_present("n"),
force_platform_mode: matches.opt_present("p"),
test_keys: matches.opt_present("t"),
unencrypted: matches.opt_present("u"),
};
if let Err(e) = hiberman::hibernate(options) {
error!("Failed to hibernate: {:?}", e);
return Err(());
}
Ok(())
}
fn resume_usage(error: bool, options: &Options) {
let brief = r#"Usage: hiberman resume [options]
Resume the system now. On success, does not return, but jumps back into the
resumed image.
"#;
print_usage(&options.usage(brief), error);
}
fn hiberman_resume(args: &mut std::env::Args) -> std::result::Result<(), ()> {
init_logging()?;
let mut opts = Options::new();
opts.optflag("h", "help", "Print this help text");
opts.optflag("n", "dry-run", "Create the hibernate image, but then exit rather than shutting down. This image should only be restored with --dry-run");
opts.optflag("p", "no-preloader", "Do not use the ImagePreloader");
opts.optflag(
"u",
"unencrypted",
"Do not encrypt the hibernate image. Use only for test and debugging",
);
opts.optflag("t", "test-keys", "Use test keys for debugging");
let args: Vec<String> = args.collect();
let matches = match opts.parse(args) {
Ok(m) => m,
Err(e) => {
error!("Failed to parse arguments: {}", e);
resume_usage(true, &opts);
return Err(());
}
};
if matches.opt_present("h") {
resume_usage(false, &opts);
return Ok(());
}
let options = ResumeOptions {
dry_run: matches.opt_present("n"),
no_preloader: matches.opt_present("p"),
test_keys: matches.opt_present("t"),
unencrypted: matches.opt_present("u"),
};
if let Err(e) = hiberman::resume(options) {
error!("Failed to resume: {:#?}", e);
return Err(());
}
Ok(())
}
fn app_usage(error: bool) {
let usage_msg = r#"Usage: hiberman subcommand [options]
This application coordinates suspend-to-disk activities. Try
hiberman <subcommand> --help for details on specific subcommands.
Valid subcommands are:
help -- Print this help text.
hibernate -- Suspend the machine to disk now.
resume -- Resume the system now.
cat -- Write a disk file contents to stdout.
cookie -- Read or write the hibernate cookie.
"#;
print_usage(usage_msg, error);
}
fn hiberman_main() -> std::result::Result<(), ()> {
let mut args = std::env::args();
if args.next().is_none() {
eprintln!("expected executable name.");
return Err(());
}
let subcommand = match args.next() {
Some(subcommand) => subcommand,
None => {
eprintln!("expected a subcommand");
return Err(());
}
};
match subcommand.as_ref() {
"--help" | "-h" | "help" => {
app_usage(false);
Ok(())
}
"cat" => hiberman_cat(&mut args),
"cookie" => hiberman_cookie(&mut args),
"hibernate" => hiberman_hibernate(&mut args),
"resume" => hiberman_resume(&mut args),
_ => {
eprintln!("unknown subcommand: {}", subcommand);
Err(())
}
}
}
fn main() {
std::process::exit(if hiberman_main().is_ok() { 0 } else { 1 });
}
#[cfg(test)]
mod tests {
//use super::*;
}