From a6ebf2d10d9015ab6874887d9a352b3abb54cdd4 Mon Sep 17 00:00:00 2001 From: Scott Lamb Date: Wed, 19 Jun 2019 17:20:44 -0700 Subject: [PATCH] add "sql" subcommand This delegates to the "sqlite3" CLI but has a couple benefits over using sqlite3 directly: * safer because it does the same locking as other moonfire-nvr invocations * more convenient because it takes the same argument format as other moonfire-nvr subcommands: * --db-dir rather than full path including /db suffix * has the --db-dir default value * --read-only rather than file:...?mode=ro Use like "moonfire-nvr sql" or "moonfire-nvr sql --read-only". --- src/cmds/mod.rs | 16 ++++++++--- src/cmds/sql.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 src/cmds/sql.rs diff --git a/src/cmds/mod.rs b/src/cmds/mod.rs index d7e5fe8..9800e5f 100644 --- a/src/cmds/mod.rs +++ b/src/cmds/mod.rs @@ -40,6 +40,7 @@ mod check; mod config; mod init; mod run; +mod sql; mod ts; mod upgrade; @@ -49,6 +50,7 @@ pub enum Command { Config, Init, Run, + Sql, Ts, Upgrade, } @@ -60,27 +62,35 @@ impl Command { Command::Config => config::run(), Command::Init => init::run(), Command::Run => run::run(), + Command::Sql => sql::run(), Command::Ts => ts::run(), Command::Upgrade => upgrade::run(), } } } -#[derive(PartialEq, Eq)] +#[derive(Copy, Clone, PartialEq, Eq)] enum OpenMode { ReadOnly, ReadWrite, Create } -/// Locks and opens the database. +/// Locks the directory without opening the database. /// The returned `dir::Fd` holds the lock and should be kept open as long as the `Connection` is. -fn open_conn(db_dir: &str, mode: OpenMode) -> Result<(dir::Fd, rusqlite::Connection), Error> { +fn open_dir(db_dir: &str, mode: OpenMode) -> Result { let dir = dir::Fd::open(db_dir, mode == OpenMode::Create)?; let ro = mode == OpenMode::ReadOnly; dir.lock(if ro { libc::LOCK_SH } else { libc::LOCK_EX } | libc::LOCK_NB) .map_err(|e| e.context(format!("db dir {:?} already in use; can't get {} lock", db_dir, if ro { "shared" } else { "exclusive" })))?; + Ok(dir) +} + +/// Locks and opens the database. +/// The returned `dir::Fd` holds the lock and should be kept open as long as the `Connection` is. +fn open_conn(db_dir: &str, mode: OpenMode) -> Result<(dir::Fd, rusqlite::Connection), Error> { + let dir = open_dir(db_dir, mode)?; let conn = rusqlite::Connection::open_with_flags( Path::new(&db_dir).join("db"), match mode { diff --git a/src/cmds/sql.rs b/src/cmds/sql.rs new file mode 100644 index 0000000..e08f031 --- /dev/null +++ b/src/cmds/sql.rs @@ -0,0 +1,71 @@ +// This file is part of Moonfire NVR, a security camera network video recorder. +// Copyright (C) 2019 Scott Lamb +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// In addition, as a special exception, the copyright holders give +// permission to link the code of portions of this program with the +// OpenSSL library under certain conditions as described in each +// individual source file, and distribute linked combinations including +// the two. +// +// You must obey the GNU General Public License in all respects for all +// of the code used other than OpenSSL. If you modify file(s) with this +// exception, you may extend this exception to your version of the +// file(s), but you are not obligated to do so. If you do not wish to do +// so, delete this exception statement from your version. If you delete +// this exception statement from all source files in the program, then +// also delete it here. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Subcommand to run a SQLite shell. + +use failure::Error; +use serde::Deserialize; +use std::process::Command; +use super::OpenMode; + +static USAGE: &'static str = r#" +Runs a SQLite shell on the Moonfire NVR database with locking. + +Usage: + + moonfire-nvr sql [options] + moonfire-nvr sql --help + +Options: + + --db-dir=DIR Set the directory holding the SQLite3 index database. + This is typically on a flash device. + [default: /var/lib/moonfire-nvr/db] + --read-only Accesses the database in read-only mode. +"#; + +#[derive(Debug, Deserialize)] +struct Args { + flag_db_dir: String, + flag_read_only: bool, +} + +pub fn run() -> Result<(), Error> { + let args: Args = super::parse_args(USAGE)?; + + let mode = if args.flag_read_only { OpenMode::ReadWrite } else { OpenMode::ReadOnly }; + let _db_dir = super::open_dir(&args.flag_db_dir, mode)?; + let mut db = format!("file:{}/db", &args.flag_db_dir); + if args.flag_read_only { + db.push_str("?mode=ro"); + } + Command::new("sqlite3").arg(&db).status()?; + Ok(()) +}