overhaul error messages

Inspired by the poor error message here:
https://github.com/scottlamb/moonfire-nvr/issues/107#issuecomment-777587727

*   print the friendlier Display version of the error rather than Debug.
    Eg, "EROFS: Read-only filesystem" rather than "Sys(EROFS)". Do this
    everywhere: on command exit, on syncer retries, and on stream
    retries.
*   print the most immediate problem and additional lines for each
    cause.
*   print the backtrace or an advertisement for RUST_BACKTRACE=1 if it's
    unavailable.
*   also mention RUST_BACKTRACE=1 in the troubleshooting guide.
*   add context in various places, including pathnames. There are surely
    many places more it'd be helpful, but this is a start.
*   allow subcommands to return failure without an Error.
    In particular, "moonfire-nvr check" does its own error printing
    because it wants to print all the errors it finds. Printing "see
    earlier errors" with a meaningless stack trace seems like it'd just
    confuse. But I also want to get rid of the misleading "Success" at
    the end and 0 return to the OS.
This commit is contained in:
Scott Lamb
2021-02-11 10:45:56 -08:00
parent ff1615a0b4
commit 9a5957d5ef
19 changed files with 101 additions and 55 deletions

View File

@@ -47,7 +47,7 @@ pub struct Args {
compare_lens: bool,
}
pub fn run(args: &Args) -> Result<(), Error> {
pub fn run(args: &Args) -> Result<i32, Error> {
// TODO: ReadOnly should be sufficient but seems to fail.
let (_db_dir, conn) = super::open_conn(&args.db_dir, super::OpenMode::ReadWrite)?;
check::run(&conn, &check::Options {

View File

@@ -54,7 +54,7 @@ pub struct Args {
db_dir: PathBuf,
}
pub fn run(args: &Args) -> Result<(), Error> {
pub fn run(args: &Args) -> Result<i32, Error> {
let (_db_dir, conn) = super::open_conn(&args.db_dir, super::OpenMode::ReadWrite)?;
let clocks = clock::RealClocks {};
let db = Arc::new(db::Database::new(clocks, conn, true)?);
@@ -77,5 +77,5 @@ pub fn run(args: &Args) -> Result<(), Error> {
siv.run();
Ok(())
Ok(0)
}

View File

@@ -41,14 +41,14 @@ pub struct Args {
db_dir: PathBuf,
}
pub fn run(args: &Args) -> Result<(), Error> {
pub fn run(args: &Args) -> Result<i32, Error> {
let (_db_dir, mut conn) = super::open_conn(&args.db_dir, super::OpenMode::Create)?;
// Check if the database has already been initialized.
let cur_ver = db::get_schema_version(&conn)?;
if let Some(v) = cur_ver {
info!("Database is already initialized with schema version {}.", v);
return Ok(());
return Ok(0);
}
// Use WAL mode (which is the most efficient way to preserve database integrity) with a large
@@ -63,5 +63,5 @@ pub fn run(args: &Args) -> Result<(), Error> {
"#)?;
db::init(&mut conn)?;
info!("Database initialized.");
Ok(())
Ok(0)
}

View File

@@ -71,7 +71,7 @@ pub struct Args {
username: String,
}
pub fn run(args: &Args) -> Result<(), Error> {
pub fn run(args: &Args) -> Result<i32, Error> {
let clocks = clock::RealClocks {};
let (_db_dir, conn) = super::open_conn(&args.db_dir, super::OpenMode::ReadWrite)?;
let db = std::sync::Arc::new(db::Database::new(clocks.clone(), conn, true).unwrap());
@@ -116,7 +116,7 @@ pub fn run(args: &Args) -> Result<(), Error> {
} else {
println!("s={}", encoded);
}
Ok(())
Ok(0)
}
fn curl_cookie(cookie: &str, flags: i32, domain: &str) -> String {

View File

@@ -59,12 +59,12 @@ fn open_dir(db_dir: &Path, mode: OpenMode) -> Result<dir::Fd, Error> {
format!("db dir {} not found; try running moonfire-nvr init",
db_dir.display())
} else {
format!("unable to open db dir {}: {}", db_dir.display(), &e)
format!("unable to open db dir {}", db_dir.display())
}))?;
let ro = mode == OpenMode::ReadOnly;
dir.lock(if ro { FlockArg::LockSharedNonblock } else { FlockArg::LockExclusiveNonblock })
.map_err(|e| e.context(format!("db dir {} already in use; can't get {} lock",
db_dir.display(), if ro { "shared" } else { "exclusive" })))?;
.map_err(|e| e.context(format!("unable to get {} lock on db dir {} ",
if ro { "shared" } else { "exclusive" }, db_dir.display())))?;
Ok(dir)
}

View File

@@ -175,7 +175,7 @@ struct Syncer {
}
#[tokio::main]
pub async fn run(args: &Args) -> Result<(), Error> {
pub async fn run(args: &Args) -> Result<i32, Error> {
let clocks = clock::RealClocks {};
let (_db_dir, conn) = super::open_conn(
&args.db_dir,
@@ -323,5 +323,5 @@ pub async fn run(args: &Args) -> Result<(), Error> {
info!("Waiting for HTTP requests to finish.");
server_handle.await??;
info!("Exiting.");
Ok(())
Ok(0)
}

View File

@@ -32,6 +32,7 @@
use failure::Error;
use std::ffi::OsString;
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::Command;
use super::OpenMode;
@@ -58,7 +59,7 @@ pub struct Args {
arg: Vec<OsString>,
}
pub fn run(args: &Args) -> Result<(), Error> {
pub fn run(args: &Args) -> Result<i32, Error> {
let mode = if args.read_only { OpenMode::ReadOnly } else { OpenMode::ReadWrite };
let _db_dir = super::open_dir(&args.db_dir, mode)?;
let mut db = OsString::new();
@@ -68,6 +69,5 @@ pub fn run(args: &Args) -> Result<(), Error> {
if args.read_only {
db.push("?mode=ro");
}
Command::new("sqlite3").arg(&db).args(&args.arg).status()?;
Ok(())
Err(Command::new("sqlite3").arg(&db).args(&args.arg).exec().into())
}

View File

@@ -43,10 +43,10 @@ pub struct Args {
timestamps: Vec<String>,
}
pub fn run(args: &Args) -> Result<(), Error> {
pub fn run(args: &Args) -> Result<i32, Error> {
for timestamp in &args.timestamps {
let t = db::recording::Time::parse(timestamp)?;
println!("{} == {}", t, t.0);
}
Ok(())
Ok(0)
}

View File

@@ -59,12 +59,13 @@ pub struct Args {
no_vacuum: bool,
}
pub fn run(args: &Args) -> Result<(), Error> {
pub fn run(args: &Args) -> Result<i32, Error> {
let (_db_dir, mut conn) = super::open_conn(&args.db_dir, super::OpenMode::ReadWrite)?;
db::upgrade::run(&db::upgrade::Args {
sample_file_dir: args.sample_file_dir.as_ref().map(std::path::PathBuf::as_path),
preset_journal: &args.preset_journal,
no_vacuum: args.no_vacuum,
}, &mut conn)
}, &mut conn)?;
Ok(0)
}