Commit 24c6aa78 authored by SamwiseFilmore's avatar SamwiseFilmore

Refactor; No provide hooks; WIP

parent 860fb85f
Pipeline #2597 failed with stage
in 2 minutes and 30 seconds
...@@ -31,19 +31,30 @@ impl<T> Node<T> { ...@@ -31,19 +31,30 @@ impl<T> Node<T> {
} }
/// A sorta thin wrapper over a Generational arena that includes /// A sorta thin wrapper over a Generational arena that includes
/// dependency relationships between nodes and some solvers /// dependency relationships between nodes and some solvers.
pub struct DepGraph<T> { pub struct DepGraph<T> {
graph: Arena<Node<T>> graph: Arena<Node<T>>
} }
impl<T> DepGraph<T> { impl<T> DepGraph<T> {
pub fn new() -> DepGraph<T> {
DepGraph {
graph: Arena::new()
}
}
/// Wrapper over `generational_arena::Arena::with_capacity` /// Wrapper over `generational_arena::Arena::with_capacity`
pub fn with_capacity(n: usize) -> DepGraph<T> { pub fn with_capacity(capacity: usize) -> DepGraph<T> {
DepGraph { DepGraph {
graph: Arena::with_capacity(n) graph: Arena::with_capacity(capacity)
} }
} }
/// Wrapper over `generaltional_arena::Arena::reserve`
pub fn reserve(&mut self, additional_capacity: usize) {
self.graph.reserve(additional_capacity);
}
/// Add an element to the graph, returning an index to the element /// Add an element to the graph, returning an index to the element
pub fn insert(&mut self, inner: T) -> Index { pub fn insert(&mut self, inner: T) -> Index {
self.graph.insert(Node::new(inner)) self.graph.insert(Node::new(inner))
...@@ -70,29 +81,36 @@ impl<T> DepGraph<T> { ...@@ -70,29 +81,36 @@ impl<T> DepGraph<T> {
/// Add a dependent relationship between a parent and a child /// Add a dependent relationship between a parent and a child
/// ///
/// Returns Err() if either of the indecies do not exist in the graph /// Returns Err(()) if either of the indecies do not exist in the graph
pub fn dependency(&mut self, parent: Index, child: Index) -> Result<(), ()> { pub fn dependency(&mut self, dependent: Index, dependency: Index) -> Result<(), ()> {
if self.graph.contains(parent) && self.graph.contains(child) { if self.graph.contains(dependent) && self.graph.contains(dependency) {
self.graph.get_mut(parent) self.graph.get_mut(dependent)
.unwrap() // Cannot be None .unwrap() // Cannot be None
.dependencies.push(child); .dependencies.push(dependency);
Ok(()) Ok(())
} else { } else {
Err(()) Err(())
} }
} }
/// This function provides a very naive and straightforward algorithm to resolve
/// a dependency graph. The Vector that is returned is a list that should be a solution
/// to the graph.
///
/// # Note
/// This function currently does NOT resolve dependency cycles or other complicated things.
/// Be careful, you'll likely end up with an infinite loop.
pub fn linear_resolve(&self) -> Vec<Index> { pub fn linear_resolve(&self) -> Vec<Index> {
let arena_len = self.graph.len(); let arena_len = self.graph.len();
let mut resolved = Vec::with_capacity(arena_len); let mut resolved = Vec::with_capacity(arena_len);
let mut seen = HashSet::with_capacity(arena_len); let mut seen = HashSet::with_capacity(arena_len);
while resolved.len() < arena_len { while resolved.len() < arena_len {
for (index, service_node) in self.graph.iter() { for (index, node) in self.graph.iter() {
// formatting? // formatting?
if !seen.contains(&index) && if !seen.contains(&index) &&
(service_node.dependencies.is_empty() || (node.dependencies.is_empty() ||
service_node.dependencies.iter().all(|index| resolved.contains(index))) node.dependencies.iter().all(|index| resolved.contains(index)))
{ {
seen.insert(index); seen.insert(index);
resolved.push(index); resolved.push(index);
...@@ -101,4 +119,10 @@ impl<T> DepGraph<T> { ...@@ -101,4 +119,10 @@ impl<T> DepGraph<T> {
} }
resolved resolved
} }
/*
pub fn grouped_resolve(&self) -> Vec<Vec<Index>> {
let mut groups = vec![vec![]];
for (index, node)
}*/
} }
use std::collections::HashMap;
use generational_arena::Index;
use dependency_graph::DepGraph;
use service::{Service, State};
pub fn graph_from_services(services: Vec<Service>) -> DepGraph<Service> {
let mut graph = DepGraph::with_capacity(services.len());
graph_add_services(&mut graph, services);
graph
}
pub fn graph_add_services(graph: &mut DepGraph<Service>, mut services: Vec<Service>) {
let services: HashMap<String, Index> = services.drain(..)
.map(|service| (service.name.clone(), graph.insert(service)) )
.collect();
for parent in services.values() {
let dependencies = 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) {
Some(child) => graph.dependency(*parent, *child)
.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
None => warn!("dependency not found: {}", dependency)
}
}
}
}
}
pub fn start_services(mut graph: DepGraph<Service>, provide_hooks: HashMap<String, impl Fn()>) {
let resolved = graph.linear_resolve();
for index in resolved.iter() {
let service = graph.get_mut(*index)
// These should all exist, we just got them out
.expect("resolved service index did not exist");
if let Some(method) = service.methods.get("start") {
if !service.state.is_running() {
method.wait();
}
} else {
error!("service {} missing 'start' method", service.name);
service.state = State::Failed;
}
//TODO: Better solution to this
// Should be able to get rid of the mutable borrow here I hope
service.state = State::Online;
if let Some(ref provides) = service.provides {
for provide in provides.iter() {
if let Some(on_provided) = provide_hooks.get(provide) {
on_provided();
}
}
}
}
}
...@@ -10,12 +10,11 @@ extern crate simple_logger; ...@@ -10,12 +10,11 @@ extern crate simple_logger;
extern crate syscall; extern crate syscall;
extern crate toml; extern crate toml;
mod dependency; mod dep_graph;
mod dependency_graph;
mod legacy; mod legacy;
mod service; mod service;
mod service_tree;
use std::collections::HashMap;
use std::env; use std::env;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{Error, Result}; use std::io::{Error, Result};
...@@ -24,8 +23,8 @@ use std::path::Path; ...@@ -24,8 +23,8 @@ use std::path::Path;
use syscall::flag::{O_RDONLY, O_WRONLY}; use syscall::flag::{O_RDONLY, O_WRONLY};
use dependency::{graph_from_services, start_services};
use service::services; use service::services;
use service_tree::ServiceTree;
fn switch_stdio(stdio: &str) -> Result<()> { fn switch_stdio(stdio: &str) -> Result<()> {
let stdin = unsafe { File::from_raw_fd( let stdin = unsafe { File::from_raw_fd(
...@@ -37,11 +36,11 @@ fn switch_stdio(stdio: &str) -> Result<()> { ...@@ -37,11 +36,11 @@ fn switch_stdio(stdio: &str) -> Result<()> {
let stderr = unsafe { File::from_raw_fd( let stderr = unsafe { File::from_raw_fd(
syscall::open(stdio, O_WRONLY).map_err(|err| Error::from_raw_os_error(err.errno))? syscall::open(stdio, O_WRONLY).map_err(|err| Error::from_raw_os_error(err.errno))?
) }; ) };
syscall::dup2(stdin.as_raw_fd(), 0, &[]).map_err(|err| Error::from_raw_os_error(err.errno))?; syscall::dup2(stdin.as_raw_fd(), 0, &[]).map_err(|err| Error::from_raw_os_error(err.errno))?;
syscall::dup2(stdout.as_raw_fd(), 1, &[]).map_err(|err| Error::from_raw_os_error(err.errno))?; syscall::dup2(stdout.as_raw_fd(), 1, &[]).map_err(|err| Error::from_raw_os_error(err.errno))?;
syscall::dup2(stderr.as_raw_fd(), 2, &[]).map_err(|err| Error::from_raw_os_error(err.errno))?; syscall::dup2(stderr.as_raw_fd(), 2, &[]).map_err(|err| Error::from_raw_os_error(err.errno))?;
Ok(()) Ok(())
} }
...@@ -53,8 +52,8 @@ pub fn main() { ...@@ -53,8 +52,8 @@ pub fn main() {
// This way we can continue to support old systems that still have init.rc // This way we can continue to support old systems that still have init.rc
if let Err(_) = fs::metadata("initfs:/etc/init.rc") { if let Err(_) = fs::metadata("initfs:/etc/init.rc") {
if let Err(err) = legacy::run(&Path::new("initfs:etc/init.rc")) { if let Err(err) = legacy::run(&Path::new("initfs:/etc/init.rc")) {
error!("failed to run initfs:etc/init.rc: {}", err); error!("failed to run initfs:/etc/init.rc: {}", err);
} }
} else { } else {
let service_list = services("initfs:/etc/init.d") let service_list = services("initfs:/etc/init.d")
...@@ -63,10 +62,11 @@ pub fn main() { ...@@ -63,10 +62,11 @@ pub fn main() {
vec![] vec![]
}); });
let service_graph = graph_from_services(service_list); let mut service_graph = ServiceTree::new();
let mut provide_hooks = HashMap::with_capacity(2); //let service_graph2 = service_graph.clone();
service_graph.push_services(service_list);
provide_hooks.insert("file:".into(), || { /*
service_graph.provide_hook("file:".to_string(), Box::new(|service_graph| {
info!("setting cwd to file:"); info!("setting cwd to file:");
if let Err(err) = env::set_current_dir("file:") { if let Err(err) = env::set_current_dir("file:") {
error!("failed to set cwd: {}", err); error!("failed to set cwd: {}", err);
...@@ -81,22 +81,23 @@ pub fn main() { ...@@ -81,22 +81,23 @@ pub fn main() {
vec![] vec![]
}); });
dependency::graph_add_services(&mut service_graph, fs_services); service_graph.push_services(fs_services);
start_services(service_graph, HashMap::new()); service_graph.start_services();
}); }));
provide_hooks.insert("display:".into(), || { service_graph.provide_hook("display:".to_string(), Box::new(|service_graph| {
switch_stdio("display:1") switch_stdio("display:1")
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
warn!("{}", err); warn!("{}", err);
}); });
}); }));*/
info!("setting PATH=initfs:/bin"); info!("setting PATH=initfs:/bin");
env::set_var("PATH", "initfs:/bin"); env::set_var("PATH", "initfs:/bin");
start_services(service_graph, provide_hooks); service_graph.start_services();
} }
// Might should not do this
syscall::setrens(0, 0).expect("init: failed to enter null namespace"); syscall::setrens(0, 0).expect("init: failed to enter null namespace");
loop { loop {
......
use std::collections::HashMap;
use generational_arena::Index;
use dep_graph::DepGraph;
use service::{Service, State};
/// Main data structure for init, containing the main interface
/// for dealing with services
pub struct ServiceTree {
graph: DepGraph<Service>,
//provide_hooks: HashMap<String, Box<FnMut(&mut ServiceTree)>>
}
impl ServiceTree {
pub fn new() -> ServiceTree {
ServiceTree {
graph: DepGraph::new(),
//provide_hooks: HashMap::new()
}
}
/*
* There are a couple of places where code like this is commented out,
* the code that exists is horribly broken. Looking for a better solution.
*/
/// Add a hook to be called after a dependency has been provided.
/// The dep can be a service's name, or anything listed in the 'provides'
/// field in a service.toml. Currently this is backed by a hashmap, so
/// it will silently overwrite an existing entry if called multiple
/// times with the same dep.
/*
pub fn provide_hook(&mut self, dep: String, hook: Box<FnMut(&mut ServiceTree)>) {
self.provide_hooks.insert(dep, hook);
}*/
/// Push some services into the graph, and add their dependency nodes.
/// Note that this does not start any services, only their metadata
/// is inserted into the graph. Metadata for services is not manipulated
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();
for parent in services.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) {
Some(child) => self.graph.dependency(*parent, *child)
.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
None => warn!("dependency not found: {}", dependency)
}
}
}
}
}
/// WIP: This function attempts to run the start method on each service in the graph
/// if it is not already running.
pub fn start_services(&mut self) {
let resolved = self.graph.linear_resolve();
for index in resolved.iter() {
let service = self.graph.get_mut(*index)
// These should all exist, we just got them out
.expect("resolved service index did not exist");
if let Some(method) = service.methods.get("start") {
if !service.state.is_running() {
method.wait();
}
} else {
error!("service {} missing 'start' method", service.name);
service.state = State::Failed;
}
//TODO: Better solution to this
// Should be able to get rid of the mutable borrow here I hope
service.state = State::Online;
/*
if let Some(on_provided) = self.provide_hooks.get(&service.name) {
on_provided(self);
}
if let Some(ref provides) = service.provides {
for provide in provides.iter() {
if let Some(on_provided) = self.provide_hooks.get(provide) {
on_provided(self);
}
}
}*/
}
}
}
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