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> {
}
/// 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> {
graph: Arena<Node<T>>
}
impl<T> DepGraph<T> {
pub fn new() -> DepGraph<T> {
DepGraph {
graph: Arena::new()
}
}
/// Wrapper over `generational_arena::Arena::with_capacity`
pub fn with_capacity(n: usize) -> DepGraph<T> {
pub fn with_capacity(capacity: usize) -> DepGraph<T> {
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
pub fn insert(&mut self, inner: T) -> Index {
self.graph.insert(Node::new(inner))
......@@ -70,29 +81,36 @@ impl<T> DepGraph<T> {
/// Add a dependent relationship between a parent and a child
///
/// Returns Err() if either of the indecies do not exist in the graph
pub fn dependency(&mut self, parent: Index, child: Index) -> Result<(), ()> {
if self.graph.contains(parent) && self.graph.contains(child) {
self.graph.get_mut(parent)
/// Returns Err(()) if either of the indecies do not exist in the graph
pub fn dependency(&mut self, dependent: Index, dependency: Index) -> Result<(), ()> {
if self.graph.contains(dependent) && self.graph.contains(dependency) {
self.graph.get_mut(dependent)
.unwrap() // Cannot be None
.dependencies.push(child);
.dependencies.push(dependency);
Ok(())
} else {
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> {
let arena_len = self.graph.len();
let mut resolved = Vec::with_capacity(arena_len);
let mut seen = HashSet::with_capacity(arena_len);
while resolved.len() < arena_len {
for (index, service_node) in self.graph.iter() {
for (index, node) in self.graph.iter() {
// formatting?
if !seen.contains(&index) &&
(service_node.dependencies.is_empty() ||
service_node.dependencies.iter().all(|index| resolved.contains(index)))
(node.dependencies.is_empty() ||
node.dependencies.iter().all(|index| resolved.contains(index)))
{
seen.insert(index);
resolved.push(index);
......@@ -101,4 +119,10 @@ impl<T> DepGraph<T> {
}
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;
extern crate syscall;
extern crate toml;
mod dependency;
mod dependency_graph;
mod dep_graph;
mod legacy;
mod service;
mod service_tree;
use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::io::{Error, Result};
......@@ -24,8 +23,8 @@ use std::path::Path;
use syscall::flag::{O_RDONLY, O_WRONLY};
use dependency::{graph_from_services, start_services};
use service::services;
use service_tree::ServiceTree;
fn switch_stdio(stdio: &str) -> Result<()> {
let stdin = unsafe { File::from_raw_fd(
......@@ -37,11 +36,11 @@ fn switch_stdio(stdio: &str) -> Result<()> {
let stderr = unsafe { File::from_raw_fd(
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(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))?;
Ok(())
}
......@@ -53,8 +52,8 @@ pub fn main() {
// 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(err) = legacy::run(&Path::new("initfs:etc/init.rc")) {
error!("failed to run initfs:etc/init.rc: {}", err);
if let Err(err) = legacy::run(&Path::new("initfs:/etc/init.rc")) {
error!("failed to run initfs:/etc/init.rc: {}", err);
}
} else {
let service_list = services("initfs:/etc/init.d")
......@@ -63,10 +62,11 @@ pub fn main() {
vec![]
});
let service_graph = graph_from_services(service_list);
let mut provide_hooks = HashMap::with_capacity(2);
provide_hooks.insert("file:".into(), || {
let mut service_graph = ServiceTree::new();
//let service_graph2 = service_graph.clone();
service_graph.push_services(service_list);
/*
service_graph.provide_hook("file:".to_string(), Box::new(|service_graph| {
info!("setting cwd to file:");
if let Err(err) = env::set_current_dir("file:") {
error!("failed to set cwd: {}", err);
......@@ -81,22 +81,23 @@ pub fn main() {
vec![]
});
dependency::graph_add_services(&mut service_graph, fs_services);
start_services(service_graph, HashMap::new());
});
provide_hooks.insert("display:".into(), || {
service_graph.push_services(fs_services);
service_graph.start_services();
}));
service_graph.provide_hook("display:".to_string(), Box::new(|service_graph| {
switch_stdio("display:1")
.unwrap_or_else(|err| {
warn!("{}", err);
});
});
}));*/
info!("setting 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");
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