Commit 2b104052 authored by SamwiseFilmore's avatar SamwiseFilmore
Browse files

WIP: Working on this :/

This was sorta a shot at doing things with an event system of sorts, and
it hasn't really gone down well. I learned things from writing this
code, but I don't think it's really useful.
parent 8e5ff06f
Pipeline #2720 passed with stage
in 3 minutes and 22 seconds
This diff is collapsed.
......@@ -2,8 +2,10 @@
name = "init"
description = "Init system for redox, includes service management"
version = "0.1.0"
edition = "2018"
[dependencies]
crossbeam = "0.6"
failure = "0.1"
generational-arena = "0.2"
log = "0.4"
......
......@@ -6,14 +6,16 @@ use generational_arena::{Arena, Index};
/// nice abstraction of a dependency graph
struct Node<T> {
inner: T,
dependencies: Vec<Index>
dependencies: Vec<Index>,
//dependents: Vec<Index>
}
impl<T> Node<T> {
fn new(inner: T) -> Node<T> {
Node {
inner,
dependencies: vec![]
dependencies: vec![],
//dependents: vec![]
}
}
......@@ -79,7 +81,7 @@ impl<T> DepGraph<T> {
.map(|node| node.unwrap() )
}
/// Add a dependent relationship between a parent and a child
/// Add a dependent-dependency 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, dependent: Index, dependency: Index) -> Result<(), ()> {
......@@ -87,12 +89,20 @@ impl<T> DepGraph<T> {
self.graph.get_mut(dependent)
.unwrap() // Cannot be None
.dependencies.push(dependency);
//self.graph.get_mut(dependency)
// .unwrap() //Cannot be None
// .dependents.push(dependent);
Ok(())
} else {
Err(())
}
}
/// Returns `None` if `dependent` does not exist, Vec can also be empty
pub fn dependencies(&self, dependent: Index) -> Option<&Vec<Index>> {
Some(&self.graph.get(dependent)?.dependencies)
}
/// 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.
......
......@@ -8,7 +8,7 @@ use std::io::{Read, Result};
use std::path::Path;
use std::process::Command;
use switch_stdio;
use crate::switch_stdio;
pub fn run(file: &Path) -> Result<()> {
let mut data = String::new();
......
//#![deny(warnings)]
extern crate failure;
extern crate generational_arena;
#[macro_use]
extern crate log;
extern crate rayon;
#[macro_use]
extern crate serde_derive;
extern crate simple_logger;
extern crate syscall;
extern crate toml;
mod dep_graph;
mod legacy;
mod service;
mod service_tree;
use std::env;
use std::fs::{self, File};
use std::io::{Error, Result};
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::path::Path;
use std::path::{Path, PathBuf};
use crossbeam::channel;
use generational_arena::Index;
use log::{error, warn};
use syscall::flag::{O_RDONLY, O_WRONLY};
use service::services;
use service_tree::ServiceTree;
use crate::service::{services, State};
use crate::service_tree::ServiceTree;
fn switch_stdio(stdio: &str) -> Result<()> {
let stdin = unsafe { File::from_raw_fd(
......@@ -45,6 +36,32 @@ fn switch_stdio(stdio: &str) -> Result<()> {
Ok(())
}
trait SchemeElement {
fn scheme(&self) -> Option<&Path>;
}
impl SchemeElement for Path {
fn scheme(&self) -> Option<&Path> {
let last = self.ancestors()
.filter(|element| element != &Path::new("") )
.last();
// lossy is fine 'cause Redox
if String::from(last?.to_string_lossy()).pop()? == ':' {
Some(last?)
} else {
None
}
}
}
pub enum Event {
RegisterServices(PathBuf),
// A service should be started if it's dependencies are satisfied
ServiceStart(Index),
// A service has changed state
ServiceChangedState(Index, State)
}
pub fn main() {
simple_logger::init()
.unwrap_or_else(|err| {
......@@ -57,51 +74,59 @@ pub fn main() {
error!("failed to run initfs:/etc/init.rc: {}", err);
}
} else {
let service_list = services("initfs:/etc/init.d")
.unwrap_or_else(|err| {
warn!("{}", err);
vec![]
});
let mut service_graph = ServiceTree::new();
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);
}
info!("setting PATH=file:/bin");
env::set_var("PATH", "file:/bin");
let fs_services = services("/etc/init.d")
.unwrap_or_else(|err| {
warn!("{}", err);
vec![]
});
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);
});
}));*/
let (transmitter, receiver) = channel::unbounded();
let mut service_graph = ServiceTree::new(transmitter.clone());
info!("setting PATH=initfs:/bin");
env::set_var("PATH", "initfs:/bin");
transmitter.send(Event::RegisterServices(PathBuf::from("initfs:/etc/init.d")))
.expect("failed to send initial event"); // Shouldn't be possible
service_graph.start_services(true);
//crossbeam::scope(|scope| {
loop {
match receiver.recv() {
Ok(event) => match event {
Event::RegisterServices(path) => {
/* Not dealing with non-canonical paths, 'cause there are
* better things to waste time on right now.
*
//TODO: This behavior should change and be more robust,
// but it will do for now
if let Some(scheme) = path.scheme() {
let mut bin_dir = scheme.to_path_buf();
bin_dir.push("/bin");
if let Ok(_) = fs::metadata(&bin_dir) {
/// Lossy is fine because it's redox, and it's a log...
info!("setting PATH={}", bin_dir.to_string_lossy());
env::set_var("PATH", bin_dir);
}
}
info!("PATH={:?}", env::var("PATH"));
*/
let service_list = services(&path)
.unwrap_or_else(|err| {
warn!("{}", err);
vec![]
});
service_graph.push_services(service_list);
},
Event::ServiceStart(index) => {
//scope.spawn(|_| {
service_graph.start_service(index);
//});
}
Event::ServiceChangedState(index, state) => {
service_graph.set_service_state(index, state);
}
},
Err(err) => error!("error recieving event: {}", err)
}
}
//});
}
// Might should not do this
syscall::setrens(0, 0).expect("init: failed to enter null namespace");
/*
loop {
let mut status = 0;
syscall::waitpid(0, &mut status, 0).unwrap();
}
}*/
}
......@@ -10,7 +10,9 @@ use std::process::Command;
//use std::thread;
use failure::Error;
use log::{error, info};
//use generational_arena::Index;
use serde_derive::Deserialize;
use toml;
#[derive(Debug)]
......@@ -60,12 +62,6 @@ impl Method {
.collect();
self.cmd = modified_cmd;
}
/* WIP
pub fn spawn(&self, channel: Sender<(Index, State)>) {
thread::spawn(move || {
});
}*/
pub fn wait(&self) {
let mut cmd = Command::new(&self.cmd[0]);
......
use std::collections::HashMap;
use crossbeam::channel::Sender;
use generational_arena::Index;
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use log::{error, info, warn};
use dep_graph::DepGraph;
use service::{Service, State};
use crate::Event;
use crate::dep_graph::DepGraph;
use crate::service::{Service, State};
/// Main data structure for init, containing the main interface
/// for dealing with services
pub struct ServiceTree {
graph: DepGraph<Service>,
// Redirection table for service names to indexes, improves insert perf
name_table: HashMap<String, Index>,
sender: Sender<Event>
//provide_hooks: HashMap<String, Box<FnMut(&mut ServiceTree)>>
}
impl ServiceTree {
pub fn new() -> ServiceTree {
pub fn new(sender: Sender<Event>) -> ServiceTree {
ServiceTree {
graph: DepGraph::new(),
name_table: HashMap::new(),
sender
//provide_hooks: HashMap::new()
}
}
......@@ -35,35 +42,89 @@ impl ServiceTree {
}*/
/// 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
/// Then send a StartService event via the `Sender` passed to `Self::new`
pub fn push_services(&mut self, mut services: Vec<Service>) {
self.graph.reserve(services.len());
let services: HashMap<String, Index> = services.drain(..)
// These iterations MUST be done separately b/c otherwise an existing
// dep might not be in the graph yet.
self.name_table = 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();
for (service_name, service_index) in self.name_table.iter() {
let service = self.graph.get(*service_index)
.expect("services were just added");
if let Some(ref dependencies) = dependencies {
if let Some(ref dependencies) = service.dependencies.clone() {
for dependency in dependencies.iter() {
match services.get(dependency) {
Some(child) => self.graph.dependency(*parent, *child)
match self.name_table.get(dependency) {
Some(dependent) => self.graph.dependency(*service_index, *dependent)
.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
// It's really a pkg/sysadmin problem at that point
None => warn!("dependency not found: {}", dependency)
}
}
}
self.sender.send(Event::ServiceStart(*service_index))
.unwrap_or_else(|err| error!("error sending start service event: {}", err));
}
}
/// Attempts to start a service. If the service has unmet dependencies,
/// it is not started and those dependencies are sent on the Sender
/// passed to `Self::new` followed by the service itself.
pub fn start_service(&self, index: Index) {
if let Some(dependencies) = self.graph.dependencies(index) {
let mut service_startable = true;
// Could be after the loop, but is helpful for error reporting
let service = self.graph.get(index)
.unwrap(); // We already checked that this index exists
if service.state.is_running() {
return ();
}
for dep_index in dependencies {
if let Some(dep) = self.graph.get(*dep_index) {
if !dep.state.is_running() {
self.sender.send(Event::ServiceStart(*dep_index))
.unwrap_or_else(|err| error!("error sending start service event: {}", err));
service_startable = false;
}
} else {
warn!("missing a dependency for service: {}", service.name);
}
}
if service_startable {
if let Some(method) = service.methods.get("start") {
if !service.state.is_running() {
method.wait();
//TODO: Somehow fix this
self.sender.send(Event::ServiceChangedState(index, State::Online));
}
} else {
error!("missing 'start' method for service: {}", service.name);
}
} else {
self.sender.send(Event::ServiceStart(index))
.unwrap_or_else(|err| error!("error sending start service event: {}", err));
}
} else {
warn!("attempted to start service that did not exist or was removed");
}
}
pub fn set_service_state(&mut self, service_index: Index, state: State) -> Option<()> {
self.graph.get_mut(service_index)?.state = state;
Some(())
}
/// 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, start_file_services: bool) {
......@@ -90,7 +151,7 @@ impl ServiceTree {
if start_file_services {
use std::env;
use service::services;
use crate::service::services;
// display:
crate::switch_stdio("display:1")
......
  • This commit is really just an experiment and probably shouldn't be merged into anything, ever. It was a good learning experience, and I've pushed it up here because I'm afraid of it getting overridden with an update. Otherwise I wouldn't have, since it's frankly an embarrassment.

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