You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
6.0 KiB
Rust
199 lines
6.0 KiB
Rust
use async_std::path::Path;
|
|
use async_std::prelude::*;
|
|
use std::cell::Cell;
|
|
use crate::utils::asyn::IntoResults;
|
|
use std::io::{ErrorKind, Error};
|
|
use async_std::fs::{read_to_string, read_dir, ReadDir, DirEntry};
|
|
use async_std::task;
|
|
use crate::config::Config;
|
|
use std::collections::HashSet;
|
|
use crate::unit::Unit;
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
pub struct LoaderConfig {
|
|
pub locations: Vec<String>,
|
|
}
|
|
|
|
impl LoaderConfig {
|
|
pub fn default() -> Self {
|
|
LoaderConfig {
|
|
locations: vec![
|
|
"etc/systemd/system/".to_string(),
|
|
"run/systemd/system/".to_string(),
|
|
"usr/lib/systemd/system/".to_string(),
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Loader {
|
|
root: String,
|
|
config: LoaderConfig,
|
|
}
|
|
|
|
impl Default for Loader {
|
|
fn default() -> Self {
|
|
Self::new("/", LoaderConfig::default())
|
|
}
|
|
}
|
|
|
|
impl Loader {
|
|
pub fn with_root(root: &str) -> Loader {
|
|
Self::new(root, LoaderConfig::default())
|
|
}
|
|
|
|
pub fn new(root: &str, config: LoaderConfig) -> Loader {
|
|
Loader { root: root.to_string(), config }
|
|
}
|
|
|
|
|
|
pub async fn load_unit(&self, unit_name: &str) -> Option<Unit> {
|
|
let config = self.load_config(unit_name).await;
|
|
let mut unit = config.get_unit()?;
|
|
let read_items = |postfix: String| {
|
|
async {
|
|
let futures = self.config
|
|
.locations
|
|
.iter()
|
|
.map(move |loc| {
|
|
let path = Path::new(&self.root).join(loc).join(format!("{}.{}", unit_name, postfix));
|
|
read_dir(path)
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.into_results()
|
|
.await
|
|
.into_iter()
|
|
.filter_map(|result: Result<ReadDir, Error>| {
|
|
result.ok()
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
let mut results = vec![];
|
|
|
|
for future in futures {
|
|
let mut reader = future;
|
|
while let Some(item) = reader.next().await {
|
|
if let Ok(item) = item {
|
|
if let Some(name) = item.file_name().to_str() {
|
|
results.push(String::from(name));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
results
|
|
}
|
|
};
|
|
|
|
unit.requires.append(&mut read_items("requires".to_string()).await);
|
|
unit.wants.append(&mut read_items("wants".to_string()).await);
|
|
|
|
Some(unit)
|
|
}
|
|
|
|
pub async fn load_config(&self, unit_name: &str) -> Config {
|
|
let mut futures = Vec::new();
|
|
let mut paths = HashSet::new();
|
|
let mut config_dirs = HashSet::new();
|
|
|
|
for location in &self.config.locations {
|
|
let current_loc = Path::new(&self.root).join(&location);
|
|
paths.insert(current_loc.join(unit_name));
|
|
|
|
let (unit_type, name) = {
|
|
let mut items = unit_name.rsplitn(2, ".");
|
|
(items.next().unwrap(), items.next().unwrap())
|
|
};
|
|
|
|
let parts: Vec<&str> = name.split("-").collect();
|
|
let mut builder = String::new();
|
|
for part_index in 0..parts.len() - 1 {
|
|
let part = parts[part_index];
|
|
builder += part;
|
|
builder += "-";
|
|
config_dirs.insert(current_loc.join(format!("{}.{}.d", builder, unit_type)));
|
|
}
|
|
|
|
// for unit test@nice.service it should also check test@.service.d
|
|
if name.contains('@') && !name.ends_with('@') {
|
|
let mut parts = name.splitn(2, '@');
|
|
config_dirs.insert(current_loc.join(format!("{}@.{}.d", parts.next().unwrap(), unit_type)));
|
|
}
|
|
|
|
config_dirs.insert(current_loc.join(unit_name.to_string() + ".d"));
|
|
}
|
|
|
|
for config_dir in config_dirs {
|
|
let dir_res: Result<ReadDir, std::io::Error> = read_dir(config_dir).await;
|
|
match dir_res {
|
|
Ok(mut dir) => {
|
|
while let Some(item) = dir.next().await {
|
|
if item.is_err() {
|
|
continue;
|
|
}
|
|
|
|
let item: DirEntry = item.unwrap();
|
|
if item.path().extension().map(|x| x == "conf").unwrap_or(false) {
|
|
paths.insert(item.path());
|
|
}
|
|
}
|
|
}
|
|
|
|
Err(err) => {
|
|
if err.kind() != ErrorKind::NotFound {
|
|
// Log?
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for location in paths {
|
|
let path = location.to_str().unwrap().to_string();
|
|
futures.push(async {
|
|
let result: Result<String, std::io::Error> = read_to_string(&path).await;
|
|
match result {
|
|
Ok(contents) => {
|
|
Some((path, contents))
|
|
}
|
|
|
|
Err(err) => {
|
|
if err.kind() != ErrorKind::NotFound {
|
|
// Log?
|
|
}
|
|
|
|
None
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
let mut results: Vec<(String, String)> = futures
|
|
.into_results().await
|
|
.into_iter()
|
|
.filter(|o| o.is_some())
|
|
.map(|x| x.unwrap())
|
|
.collect();
|
|
|
|
let mut config = Config::new(unit_name);
|
|
let mut configs = Vec::new();
|
|
|
|
results.sort();
|
|
|
|
for (path, contents) in results {
|
|
if path.ends_with(".conf") {
|
|
configs.push((path, contents));
|
|
continue;
|
|
}
|
|
|
|
config.load_file(&path, &contents).unwrap();
|
|
}
|
|
|
|
for (path, contents) in configs {
|
|
config.load_file(&path, &contents).unwrap();
|
|
}
|
|
|
|
return config;
|
|
}
|
|
} |