diff --git a/Cargo.toml b/Cargo.toml index 65c96332..3dd65457 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ lalrpop-util = "0.19" regex = "1" sexp = "1.1" thiserror = "1.0" +walkdir = "2" diff --git a/README.md b/README.md index 18a17dda..c7e72d08 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,9 @@ on the secilc package. ## casc The Cascade compiler is named casc, and will be located at target/debug/casc after a -successful build. It takes one argument, the name of a policy file to be built: +successful build. Input files are supplied as arguments. Directory arguments +are searched recursively for policy files. If no valid policy files are found, +casc will exit with an error. ``` $ casc my_policy.cas diff --git a/src/bin/casc.rs b/src/bin/casc.rs index 58053c45..ec6bde4c 100644 --- a/src/bin/casc.rs +++ b/src/bin/casc.rs @@ -6,19 +6,37 @@ use selinux_cascade::error::ErrorItem; use clap::Parser; use std::fs::File; use std::io::{Error, ErrorKind, Write}; +use walkdir::WalkDir; #[derive(Parser, Debug)] -#[clap(author, version, name = "casc")] +#[clap(author, version, name = "casc", about)] struct Args { + /// List of input files to process. Directories are searched recursively. #[clap(required(true))] input_file: Vec, } fn main() -> std::io::Result<()> { let args = Args::parse(); - let policies: Vec<&str> = args.input_file.iter().map(|s| s as &str).collect(); + let policies: Vec = match get_policy_files(args.input_file) { + Ok(mut s) => { + s.sort_unstable(); + s + } + Err(e) => { + eprintln!("{}", e); + return Err(e); + } + }; + if policies.is_empty() { + // Files supplied on command line, but no .cas files found + return Err(Error::new( + ErrorKind::InvalidData, + "No policy source files found", + )); + } let mut out_file = File::create("out.cil")?; - let res = compile_system_policy(policies); + let res = compile_system_policy(policies.iter().map(|s| s as &str).collect()); match res { Err(error_list) => { for e in error_list { @@ -35,3 +53,19 @@ fn main() -> std::io::Result<()> { Ok(s) => out_file.write_all(s.as_bytes()), } } + +// Create a list of policy files +fn get_policy_files(filenames: Vec) -> Result, Error> { + let mut policy_files = Vec::new(); + for file in filenames { + for entry in WalkDir::new(file) { + let entry = entry?; + if entry.file_type().is_file() && entry.path().extension().unwrap_or_default() == "cas" + { + let filename = entry.path().display().to_string(); + policy_files.push(filename); + } + } + } + Ok(policy_files) +}