diff --git a/src/config.rs b/src/config.rs index f2ba1e5..6c610da 100644 --- a/src/config.rs +++ b/src/config.rs @@ -93,7 +93,12 @@ impl TryFrom for Config { bc.auth.insert(s); } Element::Fork => bc.fork = true, - Element::Include(_) => { /* no-op */ } + Element::Include(_) => { + // NO-OP: removed during `Document::resolve_includes` + } + Element::Includedir(_) => { + // NO-OP: removed during `Document::resolve_includedirs` + } Element::KeepUmask => bc.keep_umask = true, Element::Limit => { warn!("busd does not implement ``"); diff --git a/src/config/xml.rs b/src/config/xml.rs index 10c0d67..2e5a377 100644 --- a/src/config/xml.rs +++ b/src/config/xml.rs @@ -1,6 +1,7 @@ use std::{ env::current_dir, - fs::read_to_string, + ffi::OsString, + fs::{read_dir, read_to_string}, path::{Path, PathBuf}, str::FromStr, }; @@ -36,7 +37,64 @@ impl Document { let mut doc = Document::from_str(&text)?; doc.file_path = Some(file_path.as_ref().to_path_buf()); - doc.resolve_includes() + doc.resolve_includedirs()?.resolve_includes() + } + + fn resolve_includedirs(self) -> Result { + let base_path = self.base_path()?; + let Document { + busconfig, + file_path, + } = self; + + let mut doc = Document { + busconfig: vec![], + file_path: None, + }; + + for el in busconfig { + match el { + Element::Includedir(dir_path) => { + let dir_path = resolve_include_path(&base_path, &dir_path); + let dir_path = match dir_path.canonicalize() { + Ok(ok) => ok, + // we treat `` as though it has `ignore_missing="yes"` + Err(err) => { + warn!( + "should canonicalize directory path '{}': {:?}", + &dir_path.display(), + err + ); + continue; + } + }; + match read_dir(&dir_path) { + Ok(ok) => { + for entry in ok { + let path = entry?.path(); + if path.extension() == Some(&OsString::from("conf")) + && path.is_file() + { + doc.busconfig.push(Element::Include(IncludeElement { + file_path: path, + ..Default::default() + })); + } + } + } + // we treat `` as though it has `ignore_missing="yes"` + Err(err) => { + warn!("should read directory '{}': {:?}", &dir_path.display(), err); + continue; + } + } + } + _ => doc.busconfig.push(el), + } + } + + doc.file_path = file_path; + Ok(doc) } fn resolve_includes(self) -> Result { @@ -56,12 +114,14 @@ impl Document { match el { Element::Include(include) => { let ignore_missing = include.ignore_missing == IncludeOption::Yes; - let file_path = match resolve_include_path(&base_path, &include.file_path) { + let file_path = resolve_include_path(&base_path, &include.file_path); + let file_path = match file_path.canonicalize().map_err(Error::msg) { Ok(ok) => ok, Err(err) => { let msg = format!( - "'{}' should be a valid file path", - include.file_path.display() + "should canonicalize file path '{}': {:?}", + &file_path.display(), + err ); if ignore_missing { warn!(msg); @@ -117,7 +177,7 @@ pub enum Element { Auth(String), Fork, Include(IncludeElement), - // TODO: support `` + Includedir(PathBuf), KeepUmask, Listen(String), Limit, @@ -255,13 +315,10 @@ pub struct TypeElement { pub r#type: BusType, } -fn resolve_include_path( - base_path: impl AsRef, - include_path: impl AsRef, -) -> Result { +fn resolve_include_path(base_path: impl AsRef, include_path: impl AsRef) -> PathBuf { let p = include_path.as_ref(); if p.is_absolute() { - return p.canonicalize().map_err(Error::msg); + return p.to_path_buf(); } error!( @@ -270,9 +327,5 @@ fn resolve_include_path( &include_path.as_ref().display() ); - base_path - .as_ref() - .join(p) - .canonicalize() - .map_err(Error::msg) + base_path.as_ref().join(p) } diff --git a/tests/config.rs b/tests/config.rs index 83a283f..25dcc92 100644 --- a/tests/config.rs +++ b/tests/config.rs @@ -12,8 +12,11 @@ fn config_read_file_with_includes_ok() { Config { auth: HashSet::from_iter(vec![String::from("ANONYMOUS"), String::from("EXTERNAL"),]), listen: HashSet::from_iter(vec![ + String::from("unix:path=/tmp/a"), // via + String::from("unix:path=/tmp/b"), // via + // should be no "unix:path=/tmp/not_included" as that file ends in .xml String::from("unix:path=/tmp/foo"), - String::from("tcp:host=localhost,port=1234"), + String::from("tcp:host=localhost,port=1234"), // via ]), policies: vec![ Policy::DefaultContext(vec![ diff --git a/tests/fixtures/includedir/a.conf b/tests/fixtures/includedir/a.conf new file mode 100644 index 0000000..d602268 --- /dev/null +++ b/tests/fixtures/includedir/a.conf @@ -0,0 +1,5 @@ + + + unix:path=/tmp/a + diff --git a/tests/fixtures/includedir/b.conf b/tests/fixtures/includedir/b.conf new file mode 100644 index 0000000..d351cfe --- /dev/null +++ b/tests/fixtures/includedir/b.conf @@ -0,0 +1,5 @@ + + + unix:path=/tmp/b + diff --git a/tests/fixtures/includedir/not_included.xml b/tests/fixtures/includedir/not_included.xml new file mode 100644 index 0000000..5af9363 --- /dev/null +++ b/tests/fixtures/includedir/not_included.xml @@ -0,0 +1,5 @@ + + + unix:path=/tmp/not_included + diff --git a/tests/fixtures/valid.conf b/tests/fixtures/valid.conf index 7a20a08..f8e2a00 100644 --- a/tests/fixtures/valid.conf +++ b/tests/fixtures/valid.conf @@ -9,4 +9,5 @@ ./valid_included.conf ./valid_missing.conf + ./includedir