lxcrond 0.2.1

cron and entr/inotify server for lxc containers
Documentation
use std::process::exit;

use getopts::Options;

use lxcrond::config::{Config, Job, VERBOSE};
use lxcrond::cron::Cron;
use lxcrond::watcher::Watcher;
use lxcrond::watcher_cron::WatcherCron;
use std::sync::atomic::Ordering;
use std::time::Duration;
use std::io::Write;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

/// Application entry point starts a single instance of Cron and either Watcher or WatcherCron.
///

fn main() {

    let args: Vec<String> = std::env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt ("c", "config",           "specify config file, default is /etc/lxcrontab", "[conf_file]");
    opts.optflag("h", "help",             "print this help menu");
    opts.optflag("v", "verbose",          "print debug information to stdout");
    opts.optflag("V", "version",          "print version number and exit");
    opts.optflag("f", "filesystem-watch", "use filesystem polling every 60 seconds to handle file jobs instead of inotify");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string())
    };

    if matches.opt_present("h") {
        print_usage(&program, opts);
    }

    if matches.opt_present("V") {
        print_version();
    }

    if matches.opt_present("v") {
        VERBOSE.store(true, Ordering::Relaxed)
    } else {
        no_hup()
    }

    // Parse the config file
    let mut cfg;
    if let Some(conf_file) = matches.opt_str("c") {
        cfg = Config::read_config(conf_file.as_str());
    } else {
        cfg = Config::read_config("/etc/lxcrontab");
    }
    if cfg.len() == 0 {
        exit(2);
    }

    // watcher is similar to entr
    if matches.opt_defined("f") {
        start_watcher_cron(cfg.take_file_jobs());
    } else {
        if let Err(jobs) = start_watcher_inotify(cfg.take_file_jobs()) {
            start_watcher_cron(jobs);
        }
    }

    // the main cron loop, this is similar to normal cron
    let cron = Cron::new(cfg.take_cron_jobs());
    std::thread::spawn(move|| {
        cron.run();
    });

    create_pid_file();

    std::thread::sleep(Duration::new(u64::max_value(), 0));

}

fn start_watcher_cron(jobs: Vec<Job>) {
    let mut watcher_cron = WatcherCron::new(jobs);
    std::thread::spawn(move || {
        watcher_cron.run()
    });
}

fn start_watcher_inotify(jobs: Vec<Job>) -> Result<(), Vec<Job>> {
    match Watcher::new(jobs) {
        (Ok(watcher), None) => std::thread::spawn(move || watcher.run()),
        (Err(_), Some(jobs)) => return Err(jobs),
        _ => {
            panic!("watcher should take the jobs or error and return them");
        }
    };
    Ok(())
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
    exit(0);
}

fn print_version() {
    println!("lxcrond {}", VERSION);
    exit(0);
}

fn no_hup() {
    unsafe {
        // ignore SIGHUP (terminal closed)
        libc::signal(1, libc::SIG_IGN);
        // ignore SIGINT (Ctrl+C)
        libc::signal(2, libc::SIG_IGN);
    }
}

fn create_pid_file() {
    let pid = std::process::id().to_string();
    if let Ok(mut file) = std::fs::File::create("/run/lxcrond.pid") {
        if let Ok(()) = file.write_all(pid.as_bytes()) {
            return;
        }
    }
    if VERBOSE.load(Ordering::Relaxed) {
        println!("could not write /run/lxcrond.pid");
    }
}