Commit 5985e1fc authored by SamwiseFilmore's avatar SamwiseFilmore

WIP: Implement most stuff

parent fb7d6277
Pipeline #2204 failed with stage
in 5 minutes and 9 seconds
use std::collections::{HashMap, HashSet};
use failure::{Error, err_msg};
use generational_arena::{Arena, Index};
use service::Service;
......@@ -20,54 +21,96 @@ impl ServiceNode {
}
}
pub fn build_graph(mut services: Vec<Service>) -> Arena<ServiceNode> {
let mut graph = Arena::with_capacity(services.len());
let services: HashMap<String, Index> = services.drain(..)
.map(|service| (service.name.clone(), graph.insert(ServiceNode::from_service(service))) )
.collect();
pub struct DepGraph {
pub graph: Arena<ServiceNode>,
on_provides: HashMap<String, fn()>
}
impl DepGraph {
pub fn on_provided(mut self, provide: impl AsRef<str>, callback: fn()) -> DepGraph {
self.on_provides.insert(provide.as_ref().to_string(), callback);
self
}
for index in services.values() {
let node = graph.get_mut(*index)
.expect("services were just added");
pub fn from_services(mut services: Vec<Service>) -> DepGraph {
let mut graph = Arena::with_capacity(services.len());
let services: HashMap<String, Index> = services.drain(..)
.map(|service| (service.name.clone(), graph.insert(ServiceNode::from_service(service))) )
.collect();
if let Some(ref dependencies) = node.service.dependencies {
for dependency in dependencies.iter() {
match services.get(dependency) {
Some(index) => node.dependencies.push(*index),
// It's not a super big deal if a dependency doesn't exist
None => warn!("dependency not found: {}", dependency)
for index in services.values() {
let node = graph.get_mut(*index)
.expect("services were just added");
if let Some(ref dependencies) = node.service.dependencies {
for dependency in dependencies.iter() {
match services.get(dependency) {
Some(index) => node.dependencies.push(*index),
// It's not a super big deal if a dependency doesn't exist
None => warn!("dependency not found: {}", dependency)
}
}
}
}
DepGraph {
graph,
on_provides: HashMap::new()
}
}
graph
}
/// Naive linear dependency resolution algorithm. The _should_
/// be a solution to the dependency graph. Note that the solution
/// is probably not deterministic.
///
/// # Limitations
/// Not currently detecting dependency cycles
/// Can't figure out
pub fn resolve_linear(graph: &Arena<ServiceNode>) -> Vec<Index> {
let arena_len = 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 graph.iter() {
// formatting?
if (service_node.dependencies.is_empty() ||
service_node.dependencies.iter().all(|index| resolved.contains(index))) &&
!seen.contains(&index) {
seen.insert(index);
resolved.push(index);
/// Naive linear dependency resolution algorithm. The _should_
/// be a solution to the dependency graph. Note that the solution
/// is probably not deterministic.
///
/// # Limitations
/// Not currently detecting dependency cycles
/// Can't figure out
fn resolve_linear(&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() {
// formatting?
if !seen.contains(&index) &&
(service_node.dependencies.is_empty() ||
service_node.dependencies.iter().all(|index| resolved.contains(index)))
{
seen.insert(index);
resolved.push(index);
}
}
}
resolved
}
resolved
/// Resolve dependencies and start them. This is not multi-threaded right now,
/// although that is the goal eventually
pub fn start(&self) -> Result<(), Error> {
let resolved = self.resolve_linear();
for index in resolved.iter() {
// These should all exist, we just got them out
let node = self.graph.get(*index).unwrap();
if let Some(method) = node.service.methods.get("start") {
method.wait();
} else {
let msg = format!("service {} missing 'start' method", node.service.name);
return Err(err_msg(msg));
}
if let Some(ref provides) = node.service.provides {
for provide in provides.iter() {
if let Some(on_provided) = self.on_provides.get(provide) {
on_provided();
}
}
}
}
Ok(())
}
}
......@@ -22,7 +22,7 @@ use std::process::Command;
use syscall::flag::{O_RDONLY, O_WRONLY};
use dependency::*;
use dependency::DepGraph;
use service::services;
fn switch_stdio(stdio: &str) -> Result<()> {
......@@ -163,20 +163,32 @@ pub fn main() {
vec![]
});
let services = build_graph(services);
let resolved = resolve_linear(&services);
env::set_var("PATH", "/bin");
let services = DepGraph::from_services(services)
.on_provided("file:", || {
info!("setting cwd to file:");
if let Err(err) = env::set_current_dir("file:") {
error!("failed to set cwd: {}", err);
}
info!("setting PATH=file:/bin");
env::set_var("PATH", "file:/bin");
// This file has had the services removed now
if let Err(err) = run(&Path::new("initfs:etc/init.rc")) {
error!("failed to run initfs:etc/init.rc: {}", err);
}
})
.on_provided("display:", || {
switch_stdio("display:1")
.unwrap_or_else(|err| {
warn!("{}", err);
});
});
for index in resolved.iter() {
let node = services.get(*index).unwrap();
node.service.methods.get("start").unwrap().wait();
}
info!("setting PATH=initfs:/bin");
env::set_var("PATH", "initfs:/bin");
// This file has had the services removed now
if let Err(err) = run(&Path::new("initfs:etc/init.rc")) {
println!("init: failed to run initfs:etc/init.rc: {}", err);
}
services.start().expect("failed to start services");
syscall::setrens(0, 0).expect("init: failed to enter null namespace");
......
......@@ -54,6 +54,7 @@ pub struct Service {
pub name: String,
pub dependencies: Option<Vec<String>>,
pub provides: Option<Vec<String>>,
pub methods: HashMap<String, Method>
}
......@@ -89,11 +90,19 @@ pub fn services(dir: impl AsRef<Path>) -> Result<Vec<Service>, Error> {
let mut services = vec![];
for file in read_dir(dir)? {
let file_path = file?.path();
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),
......
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