Commit 72791b4d authored by SamwiseFilmore's avatar SamwiseFilmore

Fix Provides; Refactoring

I'm pretty happy with the state of this at this point. It's not ideal,
and I'm certainly not solving all the problems I wanted to, but I'm
making tangible progress.

Still to do includes refactoring into an event system to better suit
async service starting and dependency resolution.
parent ef1371ed
Pipeline #2703 passed with stage
in 2 minutes and 39 seconds
......@@ -14,7 +14,7 @@ use std::path::{Path, PathBuf};
use log::error;
use syscall::flag::{O_RDONLY, O_WRONLY};
use crate::service::services;
use crate::service::Service;
use crate::service_tree::ServiceTree;
const INITFS_SERVICE_DIR: &str = "initfs:/etc/init.d";
......@@ -44,23 +44,6 @@ trait PathExt {
impl PathExt for Path {
//TODO: Could be better written, gross indexing
fn scheme(&self) -> Option<PathBuf> {
/*
let last = self//.as_ref()
.ancestors()
.filter(|element| element != &Path::new("") )
.last();
// lossy is fine 'cause Redox
let last = String::from(last?.to_string_lossy());
let last_len: usize = last.len();
// Redox returns `file:/` as the last, not `file:`
if last.get(last_len - 1usize)? == ":" {
Some(Path::new(&last))
} else if (last.get(last_len - 1usize)? == "/") && (last.get(last_len - 2usize)? == ":") {
Some(Path::new(last.get(0usize..last_len - 1usize)?))
} else {
None
}*/
let path = self.as_os_str()
.to_string_lossy();
......@@ -84,7 +67,7 @@ pub fn main() {
error!("failed to run initfs:/etc/init.rc: {}", err);
}
} else {
let service_list = services(INITFS_SERVICE_DIR)
let service_list = Service::from_dir(INITFS_SERVICE_DIR)
.unwrap_or_else(|err| {
error!("error parsing service directory '{}': {}", INITFS_SERVICE_DIR, err);
vec![]
......
......@@ -6,12 +6,9 @@ use std::fs::{File, read_dir};
use std::io::Read;
use std::path::{Path, PathBuf};
use std::process::Command;
//use std::sync::mpsc::Sender;
//use std::thread;
use failure::{err_msg, Error};
use log::{error, info};
//use generational_arena::Index;
use log::{debug, error, info};
use serde_derive::Deserialize;
use toml;
......@@ -98,7 +95,7 @@ impl Method {
cmd.current_dir(cwd);
}
info!("waiting on {:?}", cmd);
debug!("waiting on {:?}", cmd);
cmd.spawn()?
.wait()?;
......@@ -123,7 +120,7 @@ pub struct Service {
}
impl Service {
/// Parse a service file
/// Parse a service file, no specific requirements for filetype.
pub fn from_file(file_path: impl AsRef<Path>) -> Result<Service, Error> {
let file_path = file_path.as_ref();
......@@ -135,23 +132,49 @@ impl Service {
//BUG: Only removes the portion after the last '.'
service.name = file_path.file_stem()
.expect("file name empty") // shouldn't be able to happen
.ok_or(err_msg("service file path missing filename"))?
.to_string_lossy() // Redox uses unicode, this should never fail
.into();
service.sub_env();
// Assume that the scheme this service came from is the one
// that the service should be started in.
// that the service should be started in
if let None = service.cwd {
// Only if it's a canonical path though
if let Some(scheme) = file_path.scheme() {
info!("setting service '{}' cwd to {}", service.name, scheme);
service.cwd = Some(scheme);
}
}
Ok(service)
}
/// Parse all the toml files in a directory as services
pub fn from_dir(dir: impl AsRef<Path>) -> Result<Vec<Service>, Error> {
let mut services = vec![];
for file in read_dir(&dir)? {
let file_path = match file {
Ok(file) => file,
Err(err) => {
error!("{}", err);
continue
}
}.path();
let is_toml = match file_path.extension() {
Some(ext) => ext == OsStr::new("toml"),
None => false
};
if is_toml {
match Service::from_file(file_path) {
Ok(service) => services.push(service),
Err(err) => error!("error parsing service file '{:#?}': {}", dir.as_ref(), err)
}
}
}
Ok(services)
}
/// Substitue all fields which support environment variable
/// substitution
fn sub_env(&mut self) {
......@@ -178,31 +201,3 @@ impl Service {
}
}
}
/// Parse all the toml files in a directory as services
pub fn services(dir: impl AsRef<Path>) -> Result<Vec<Service>, Error> {
let mut services = vec![];
for file in read_dir(&dir)? {
let file_path = match file {
Ok(file) => file,
Err(err) => {
error!("{}", err);
continue
}
}.path();
let is_toml = match file_path.extension() {
Some(ext) => ext == OsStr::new("toml"),
None => false
};
if is_toml {
match Service::from_file(file_path) {
Ok(service) => services.push(service),
Err(err) => error!("error parsing service file '{:#?}': {}", dir.as_ref(), err)
}
}
}
Ok(services)
}
......@@ -4,20 +4,25 @@ use generational_arena::Index;
use log::{error, warn};
use crate::dep_graph::DepGraph;
use crate::service::{Service, services};
use crate::service::Service;
const FS_SERVICE_DIR: &str = "file:/etc/init.d";
/// Main data structure for init, containing the main interface
/// for dealing with services
pub struct ServiceTree {
graph: DepGraph<Service>
graph: DepGraph<Service>,
// Must be sorta global so that dependencies across `push_services`
// boundaries link up correctly.
redirect_map: HashMap<String, Index>
}
impl ServiceTree {
pub fn new() -> ServiceTree {
ServiceTree {
graph: DepGraph::new()
graph: DepGraph::new(),
redirect_map: HashMap::new()
}
}
......@@ -27,20 +32,33 @@ impl ServiceTree {
pub fn push_services(&mut self, mut services: Vec<Service>) {
self.graph.reserve(services.len());
let services: HashMap<String, Index> = services.drain(..)
.map(|service| (service.name.clone(), self.graph.insert(service)) )
.collect();
// This is sorta ugly, but provides have to work
for service in services.drain(..) {
let name = service.name.clone();
let provides = service.provides.clone();
let index = self.graph.insert(service);
if let Some(mut provides) = provides {
self.redirect_map.reserve(provides.len() + 1);
for provide in provides.drain(..) {
self.redirect_map.insert(provide, index);
}
}
self.redirect_map.insert(name, index);
}
for parent in services.values() {
//TODO: Only iterate over the services that are being added
for parent in self.redirect_map.values() {
let dependencies = self.graph.get(*parent)
.expect("services were just added")
.dependencies.clone();
if let Some(ref dependencies) = dependencies {
for dependency in dependencies.iter() {
match services.get(dependency) {
match self.redirect_map.get(dependency) {
Some(child) => self.graph.dependency(*parent, *child)
.unwrap_or_else(|_| warn!("failed to add dependency") ),
.unwrap_or_else(|()| warn!("failed to add dependency") ),
// It's not a super big deal if a dependency doesn't exist
// I mean, it is, but IDK what to do in that situation
// It's really a pkg problem at that point
......@@ -78,20 +96,12 @@ impl ServiceTree {
}*/
if provides.contains(&"file:".to_string()) {
let fs_services = services(FS_SERVICE_DIR)
let fs_services = Service::from_dir(FS_SERVICE_DIR)
.unwrap_or_else(|err| {
error!("error parsing service directory '{}': {}", FS_SERVICE_DIR, err);
vec![]
});
// This is surely a poor descision, should probably be
// made on a per-service basis
/*
info!("setting cwd to 'file:'");
if let Err(err) = env::set_current_dir("file:") {
error!("failed to set cwd: {}", err);
}*/
self.push_services(fs_services);
self.start_services();
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment