blob: 2d63ce99d9bd10cd075a495a59c442e67b96c928 [file] [log] [blame] [edit]
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! High level support for creating and opening the files used by hibernate.
use std::fs::create_dir;
use std::fs::remove_file;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Seek;
use std::io::Write;
use std::path::Path;
use anyhow::Context;
use anyhow::Result;
use log::warn;
/// Define the directory where hibernate state files are kept.
pub const HIBERMETA_DIR: &str = "/mnt/hibermeta";
/// Define the ramfs location where ephemeral files are stored that should not
/// persist across even an unexpected reboot.
pub const TMPFS_DIR: &str = "/run/hibernate/";
/// Define the name of the token file indicating resume is in progress. Note:
/// Services outside of hiberman use this file, so don't change this name
/// carelessly.
const RESUME_IN_PROGRESS_FILE: &str = "resume_in_progress";
/// Define the attempts count file name.
const HIBER_ATTEMPTS_FILE_NAME: &str = "attempts_count";
/// Define the hibernate failures count file name.
const HIBER_FAILURES_FILE_NAME: &str = "hibernate_failures";
/// Define the resume failures count file name.
const RESUME_FAILURES_FILE_NAME: &str = "resume_failures";
/// Open a metrics file.
fn open_cumulative_metrics_file(path: &Path) -> Result<File> {
let file = File::options()
.read(true)
.write(true)
.create(true)
.open(path)
.context("Cannot open metrics file")?;
Ok(file)
}
/// Open the attempts_count file, to keep track of the number of hibernate
/// attempts for metric tracking purposes.
pub fn open_attempts_file() -> Result<File> {
let path = Path::new(HIBERMETA_DIR).join(HIBER_ATTEMPTS_FILE_NAME);
open_cumulative_metrics_file(&path)
}
/// Open the hibernate_failures file, to keep track of the number of hibernate
/// failures for metric tracking purposes.
pub fn open_hiber_fails_file() -> Result<File> {
let path = Path::new(HIBERMETA_DIR).join(HIBER_FAILURES_FILE_NAME);
open_cumulative_metrics_file(&path)
}
/// Open the resume_failures file, to keep track of the number of resume
/// failures for metric tracking purposes.
pub fn open_resume_failures_file() -> Result<File> {
let path = Path::new(HIBERMETA_DIR).join(RESUME_FAILURES_FILE_NAME);
open_cumulative_metrics_file(&path)
}
/// Read the given metrics file
pub fn read_metric_file(file: &mut File) -> Result<String> {
let mut value_str = String::new();
file.read_to_string(&mut value_str)
.context("Failed to parse metric value")?;
Ok(value_str)
}
/// Increment the value in the counter file
pub fn increment_file_counter(file: &mut File) -> Result<()> {
let value_str = read_metric_file(file)?;
let mut value: u32 = value_str.parse().unwrap_or(0);
value += 1;
file.rewind()?;
file.write_all(value.to_string().as_bytes())
.context("Failed to increment counter")
}
/// Add the resuming file token that other services can check to quickly see if
/// a resume is in progress.
pub fn create_resume_in_progress_file() -> Result<()> {
if !Path::new(TMPFS_DIR).exists() {
create_dir(TMPFS_DIR).context("Cannot create tmpfs directory")?;
}
let rip_path = Path::new(TMPFS_DIR).join(RESUME_IN_PROGRESS_FILE);
if rip_path.exists() {
warn!("{} unexpectedly already exists", rip_path.display());
}
OpenOptions::new()
.write(true)
.create(true)
.open(rip_path)
.context("Failed to create resume token file")?;
Ok(())
}
/// Remove the resume_in_progress file if it exists. A result is not returned
/// because besides logging (done here) there's really no handling of this error
/// that could be done.
pub fn remove_resume_in_progress_file() {
let rip_path = Path::new(TMPFS_DIR).join(RESUME_IN_PROGRESS_FILE);
if rip_path.exists() {
if let Err(e) = remove_file(&rip_path) {
warn!("Failed to remove {}: {}", rip_path.display(), e);
if rip_path.exists() {
warn!("{} still exists!", rip_path.display());
}
}
}
}