Skip to content
This repository has been archived by the owner on Dec 31, 2022. It is now read-only.

Commit

Permalink
Fix crashes after restart (#32)
Browse files Browse the repository at this point in the history
Fix failed assertion after restart.

Fix Nginx crash loop caused by invalid DNS name.

The DNS name of a remote camera is validated before adding it to the database, but it's still possible for that name to change afterwards. In such cases, the corresponding Nginx config snippets written by lcsvc contained the no-longer-valid DNS name, which caused Nginx to fail to reload and, after a reboot, fail to start.

Fix is twofold. First, skip writing Nginx config snippets when remote cam is inaccessible. Second, relocate Nginx config snippets to /run. This ensures that, after a reboot, Nginx does not load potentially invalid configs before lcsvc has a chance to validate.
  • Loading branch information
reynoldsbd authored Dec 27, 2019
1 parent 440a1f6 commit 042e144
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 38 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "lunacam"
version = "1.0.0"
version = "1.0.1"
authors = ["Bobby Reynolds <bobby@reynoldsbd.net>"]
license = "MIT OR Apache-2.0"
edition = "2018"
Expand Down
34 changes: 26 additions & 8 deletions src/cameras.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
//! Camera management

use std::env;
use std::fs;
use std::path::Path;
use std::sync::RwLock;

use actix_web::http::{StatusCode};
use actix_web::web::{self, Data, Json, ServiceConfig};
use diesel::prelude::*;
use log::{debug, error, info, trace};
use log::{debug, error, info, trace, warn};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use tera::{Context, Tera};
Expand Down Expand Up @@ -92,7 +91,7 @@ fn put_camera(
.first(&conn)?;

if camera.enabled {
write_proxy_config(&camera, &templates)
write_proxy_config(&camera, &client, &templates)
.and_then(|_| proxy::reload())
.unwrap_or_else(|e|
error!("failed to configure proxy for camera {}: {}", camera.id, e)
Expand Down Expand Up @@ -260,7 +259,7 @@ fn patch_camera(
.set(&camera)
.execute(&conn)?;
if camera.enabled {
write_proxy_config(&camera, &templates)
write_proxy_config(&camera, &client, &templates)
.unwrap_or_else(|e|
error!("failed to configure proxy for camera {}: {}", camera.id, e)
);
Expand Down Expand Up @@ -335,15 +334,32 @@ pub fn configure_api(service: &mut ServiceConfig) {
/// Gets path of the proxy configuration file for the specified camera
fn get_proxy_config_path(id: i32) -> Result<impl AsRef<Path>> {

let state_dir = env::var("STATE_DIRECTORY")?;
let path = format!("{}/nginx/proxy-{}.conf", state_dir, id);
let cfg_dir = proxy::config_dir()?;
let path = format!("{}/proxy-{}.conf", cfg_dir, id);

Ok(path)
}


/// Writes or removes the proxy configuration file for this camera
fn write_proxy_config(camera: &Camera, templates: &Tera) -> Result<()> {
fn write_proxy_config(
camera: &Camera,
client: &Client,
templates: &Tera
) -> Result<()> {

// Validate remote address before writing proxy config (otherwise, Nginx
// will fail to start/reload, potentially making the web UI inaccessible)
if !camera.local {
debug!("validating connection to camera {}", camera.id);
let url = format!("http://{}/api/stream", camera.address);
if let Err(err) = client.get(&url).send() {
// Misconfigured camera should not bring down the whole system
error!("failed to connect to camera {}: {}", camera.id, err);
warn!("skipping proxy configuration");
return Ok(());
}
}

let mut context = Context::new();
context.insert("camera", camera);
Expand Down Expand Up @@ -383,6 +399,7 @@ fn clear_proxy_config(id: i32) -> Result<()> {
/// in this module.
pub fn initialize(
conn: &PooledConnection,
client: &Client,
templates: &Tera,
#[cfg(feature = "stream")]
stream: &Stream,
Expand All @@ -392,7 +409,7 @@ pub fn initialize(

for camera in &cameras {
if camera.enabled {
write_proxy_config(&camera, templates)
write_proxy_config(&camera, client, templates)
.unwrap_or_else(|e|
error!("failed to configure proxy for camera {}: {}", camera.id, e)
);
Expand All @@ -410,6 +427,7 @@ pub fn initialize(
#[cfg(feature = "stream")]
{
let local_cam_count = cameras.iter()
.filter(|c| c.local)
.count();

assert!(local_cam_count <= 1);
Expand Down
4 changes: 1 addition & 3 deletions src/lcsvc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use tera::Tera;
use lunacam::cameras;
use lunacam::db;
use lunacam::error::Result;
use lunacam::proxy;
use lunacam::stream;
use lunacam::ui;
use lunacam::users;
Expand Down Expand Up @@ -58,8 +57,6 @@ fn main() -> Result<()> {

init_logging();

proxy::init()?;

let client = Data::new(Client::new());
let templates = Data::new(load_templates()?);
let pool = Data::new(db::connect()?);
Expand All @@ -73,6 +70,7 @@ fn main() -> Result<()> {
if cfg!(feature = "portal") {
cameras::initialize(
&conn,
&client,
&templates,
#[cfg(feature = "stream")]
&stream,
Expand Down
36 changes: 18 additions & 18 deletions src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,6 @@ use log::{debug, trace, warn};
use crate::error::Result;


/// Ensures the proxy config directory exists
pub fn init() -> Result<()> {

trace!("identifying proxy config directory");
let state_dir = env::var("STATE_DIRECTORY")?;

let config_dir = format!("{}/nginx", state_dir);
trace!("checking for presence of proxy config directory {}", config_dir);
if fs::metadata(&config_dir).is_err() {

debug!("creating proxy config dir {}", config_dir);
fs::create_dir_all(&config_dir)?;
}

Ok(())
}


/// Reloads server configuration
pub fn reload() -> Result<()> {

Expand All @@ -46,3 +28,21 @@ pub fn reload() -> Result<()> {

Ok(())
}


/// Retrieves the proxy configuration directory, creating it if it does not yet
/// exist.
pub fn config_dir() -> Result<String> {

trace!("identifying proxy config directory");

let rt_dir = env::var("RUNTIME_DIRECTORY")?;
let cfg_dir = format!("{}/nginx", rt_dir);

if fs::metadata(&cfg_dir).is_err() {
debug!("creating proxy config directory {}", cfg_dir);
fs::create_dir_all(&cfg_dir)?;
}

Ok(cfg_dir)
}
9 changes: 3 additions & 6 deletions src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use std::env;
use std::fs;
use std::io::Write;
use std::path::Path;
use std::process::{Command, Stdio};
use std::sync::RwLock;

Expand Down Expand Up @@ -141,12 +140,10 @@ fn make_command(_orientation: Orientation) -> Result<Command> {


/// Gets the location of the HLS stream's proxy configuration file
fn get_proxy_config_path() -> Result<impl AsRef<Path>> {
fn get_proxy_config_path() -> Result<String> {

trace!("identifying proxy config directory");
let state_dir = env::var("STATE_DIRECTORY")?;

let path = format!("{}/nginx/hls.conf", state_dir);
let cfg_dir = proxy::config_dir()?;
let path = format!("{}/hls.conf", cfg_dir);

Ok(path)
}
Expand Down
2 changes: 1 addition & 1 deletion tools/pi-gen/common/02-web/files/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ http {
include /usr/share/lunacam/nginx/*.conf;

# HLS reverse proxies
include /var/lib/lunacam/nginx/*.conf;
include /run/lunacam/nginx/*.conf;

# LunaCam API
location / {
Expand Down
1 change: 1 addition & 0 deletions tools/pi-gen/common/03-lcsvc/files/lcsvc.service
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Description=LunaCam Service

[Service]
User=lunacam
RuntimeDirectory=lunacam
StateDirectory=lunacam
Environment=LC_TEMPLATES=/usr/share/lunacam/templates
ExecStart=/usr/bin/lcsvc
Expand Down

0 comments on commit 042e144

Please sign in to comment.