summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--init/src/host/locale.rs12
-rw-r--r--init/src/host/timezone.rs2
-rw-r--r--init/src/main.rs12
-rw-r--r--init/src/services/mod.rs2
-rw-r--r--init/src/services/unit_parser.rs166
-rw-r--r--init/src/services/units.rs293
6 files changed, 309 insertions, 178 deletions
diff --git a/init/src/host/locale.rs b/init/src/host/locale.rs
index d955297..3d9b11d 100644
--- a/init/src/host/locale.rs
+++ b/init/src/host/locale.rs
@@ -1,6 +1,6 @@
// NOTE: Ts file contains setting localisation by environment variables
// Main logic implemented but how i think it can contain with broken logic or checks
-// At least if check failed it set C.UTF8 (fallback locale)
+// At least if check failed it set C.UTF8 (fallback locale)
// And why is std::env::set_var unsafe????
use crate::host::timezone::set_timezone;
@@ -10,13 +10,13 @@ use std::io::Write;
use std::process::Command;
/// Main function which setting system locale.
-///
+///
/// Logic && Checks
-/// 1. Reading /etc/default/locale to find needed language.
+/// 1. Reading /etc/default/locale to find needed language.
/// If it broken/has syntax errors skipping this step
-/// 2. Checking for locale avalible (also switching `-` and `_`),
-/// if not uses fallback locale.
-///
+/// 2. Checking for locale avalible (also switching `-` and `_`),
+/// if not uses fallback locale.
+///
pub fn set_locale(locale: Option<String>) -> Result<(), Box<dyn std::error::Error>> {
let loc = match locale {
Some(l) => l,
diff --git a/init/src/host/timezone.rs b/init/src/host/timezone.rs
index 1062a8b..2da9e85 100644
--- a/init/src/host/timezone.rs
+++ b/init/src/host/timezone.rs
@@ -1,6 +1,6 @@
use crate::log::*;
use std::fs;
-
+
pub fn set_timezone(timezone: Option<String>) -> Result<(), Box<dyn std::error::Error>> {
let tz = match timezone {
Some(tz) => tz,
diff --git a/init/src/main.rs b/init/src/main.rs
index 40e47a4..fa7d086 100644
--- a/init/src/main.rs
+++ b/init/src/main.rs
@@ -4,8 +4,8 @@ mod log;
mod mounts;
mod pid_one;
mod processes;
-mod signals;
mod services;
+mod signals;
use crate::host::locale;
use crate::host::set::set_hostname;
@@ -15,17 +15,19 @@ use crate::mounts::fstab::FstabEntry;
use crate::mounts::rescue;
use crate::pid_one::check_pid;
use crate::processes::udev::spawn_udev;
-use crate::services::unit_parser::Runlevel;
+use crate::services::units::{Runlevel, services_mainloop};
use crate::signals::sigchld;
// RULE: I will not use .expect() and .unwrap() in this project. This causes panic,
// which will affect stability.
-use std::sync::Mutex;
+use std::sync::Mutex;
+use std::thread;
/// Variable which show current runlevel;
-pub static mut RUNLEVEL_STATE: Mutex<Runlevel> = Mutex::new(Runlevel::Undefined);
+pub static RUNLEVEL_STATE: Mutex<Runlevel> = Mutex::new(Runlevel::Undefined);
+// TODO: Add proper RUNLEVEL_STATE switching
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Initializing your system.");
if let Err(e) = check_pid() {
@@ -77,5 +79,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let _ = sigchld::setup_sigchld_handler();
+ thread::spawn(move || services_mainloop());
+
Ok(())
}
diff --git a/init/src/services/mod.rs b/init/src/services/mod.rs
index bf86a6a..f16e62e 100644
--- a/init/src/services/mod.rs
+++ b/init/src/services/mod.rs
@@ -1 +1 @@
-pub mod unit_parser;
+pub mod units;
diff --git a/init/src/services/unit_parser.rs b/init/src/services/unit_parser.rs
deleted file mode 100644
index 1e2bcc7..0000000
--- a/init/src/services/unit_parser.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-// This module defines unit settings parsing logig
-//
-// NOTE: ON UNIT PARSING LOGIC
-// when parsing a unit in /etc/vigil/units/ we shoul ignore
-// ANY broken/bad constructed toml unit configuration file
-// without "crashing" logic.
-//
-// NOTE: ON GENERAL SERVICE LOGGING
-// when vigil starts service, it should send out logs to vigil?
-// or logs in such init systems are just taking the stdout+stderr
-// of service and showing its output? idk 4 now, look into how its supposed to be
-
-use crate::{log_warning, RUNLEVEL_STATE};
-use serde::Deserialize;
-use std::fs::{read_dir, read_to_string};
-
-/*
- [info]
- name = "name"
- description = "description"
- version = "version"
-
- [config]
- exec = "path to exec"
- runlevel =
- restart=always|never
-
-*/
-
-#[derive(Deserialize)]
-enum Restart {
- #[serde(alias = "always")]
- Always,
- #[serde(alias = "never")]
- Never,
-}
-
-#[derive(Deserialize, PartialEq, Eq)]
-pub enum Runlevel {
- /// The system is shutting down, runlevel int: 0
- Shutdown,
- /// One-user system debug-mode, runlevel int: 1
- OneUser,
- /// Multi-user CLI (TTY) with no network, runlevel int: 2
- MultiNoNetwork,
- /// Multi-user CLI with network, runlevel int: 3
- MultiNetwork,
- /// Multi-user mode with GUI, runlevel int: 5
- MultiUserGUINetwork,
- /// Runlevel is not critical for running the service, runlevel int: 4
- Undefined,
- /// System going too reboot, runlevel int: 6
- Reboot,
-}
-
-#[allow(dead_code)]
-#[derive(Deserialize)]
-pub struct ServiceInfo {
- /// Service name
- name: String,
- /// Service description
- description: String,
- /// Service version
- version: String,
-}
-
-#[allow(dead_code)]
-#[derive(Deserialize)]
-pub struct ServiceConfig {
- /// Execution command, like ExecStart in sysd
- exec: String,
- /// Runlevel, like after=*.target
- runlevel: Runlevel,
- /// Restart service: Always|Never
- restart: Restart,
-}
-
-/// Main Unit-file struct
-#[allow(dead_code)]
-#[derive(Deserialize)]
-pub struct Unit {
- info: ServiceInfo,
- config: ServiceConfig,
-}
-
-#[allow(dead_code)]
-impl Restart {
- pub fn as_str(&self) -> &'static str {
- match self {
- Restart::Always => "always",
- Restart::Never => "never",
- }
- }
-}
-
-/// Function which starting services declared in `/etc/vigil/units/`
-/// Based on the global runlevel variable, should be runned as second thread
-pub fn execute_services() -> Result<(), Box<dyn std::error::Error>> {
- let unit_list = parse_all_units()?;
-
- loop {
- match *RUNLEVEL_STATE.try_lock()? {
- Runlevel::Undefined => {
- for unit in &unit_list {
- if unit.config.runlevel == Runlevel::Undefined {
- std::process::Command::new(unit.config.exec.clone());
- } else {
- continue;
- }
- }
- }
- Runlevel::Reboot => {
- todo!();
- }
-
- Runlevel::Shutdown => {
- todo!();
- }
-
- Runlevel::OneUser => {
- todo!();
- }
-
- Runlevel::MultiNoNetwork => {
- todo!();
- }
-
- Runlevel::MultiNetwork => {
- todo!();
- }
-
- Runlevel::MultiUserGUINetwork => {
- todo!();
- }
- }
- }
-}
-
-#[allow(dead_code)]
-fn parse_all_units() -> Result<Vec<Unit>, Box<dyn std::error::Error>> {
- let mut units: Vec<Unit> = Vec::new();
-
- for unit in read_dir("/etc/vigil/units")? {
- let unit_path = unit?.path();
- let unit_str: String = match read_to_string(unit_path) {
- Ok(content) => content,
- Err(e) => {
- log_warning(&format!("Error while reading unit: {}", e));
- continue;
- }
- };
-
- let deserialized: Result<Unit, _> = toml::from_str(&unit_str);
- match deserialized {
- Ok(unit) => {
- units.push(unit);
- }
- Err(e) => {
- log_warning(&format!("Error while parsing unit: {}", e));
- continue;
- }
- };
- }
-
- Ok(units)
-}
diff --git a/init/src/services/units.rs b/init/src/services/units.rs
new file mode 100644
index 0000000..01c4183
--- /dev/null
+++ b/init/src/services/units.rs
@@ -0,0 +1,293 @@
+// This module defines unit settings parsing logig
+//
+// NOTE: ON UNIT PARSING LOGIC
+// when parsing a unit in /etc/vigil/units/ we shoul ignore
+// ANY broken/bad constructed toml unit configuration file
+// without "crashing" logic.
+//
+// NOTE: ON GENERAL SERVICE LOGGING
+// when vigil starts service, it should send out logs to vigil?
+// or logs in such init systems are just taking the stdout+stderr
+// of service and showing its output? idk 4 now, look into how its supposed to be
+
+use crate::{RUNLEVEL_STATE, log_warning};
+use serde::Deserialize;
+use std::{
+ fs::{read_dir, read_to_string},
+ process::Child,
+ time::Duration,
+};
+
+/*
+ [info]
+ name = "name"
+ description = "description"
+ version = "version"
+
+ [config]
+ exec = "path to exec"
+ runlevel =
+ restart=always|never
+
+*/
+
+#[derive(Deserialize, PartialEq, Clone)]
+enum Restart {
+ #[serde(alias = "always")]
+ Always,
+ #[serde(alias = "never")]
+ Never,
+}
+
+#[derive(Deserialize, PartialEq, Eq)]
+pub enum Runlevel {
+ /// The system is shutting down, runlevel int: 0
+ Shutdown,
+ /// One-user system debug-mode, runlevel int: 1
+ OneUser,
+ /// Multi-user CLI (TTY) with no network, runlevel int: 2
+ MultiNoNetwork,
+ /// Multi-user CLI with network, runlevel int: 3
+ MultiNetwork,
+ /// Multi-user mode with GUI, runlevel int: 5
+ MultiGUINetwork,
+ /// Runlevel is not critical for running the service, runlevel int: 4
+ Undefined,
+ /// System going too reboot, runlevel int: 6
+ Reboot,
+}
+
+#[allow(dead_code)]
+#[derive(Deserialize)]
+pub struct ServiceInfo {
+ /// Service name
+ name: String,
+ /// Service description
+ description: String,
+ /// Service version
+ version: String,
+}
+
+#[allow(dead_code)]
+#[derive(Deserialize)]
+pub struct ServiceConfig {
+ /// Execution command, like ExecStart in sysd
+ exec: String,
+ /// Runlevel, like after=*.target
+ runlevel: Runlevel,
+ /// Restart service: Always|Never
+ restart: Restart,
+}
+
+/// Main Unit-file struct
+#[allow(dead_code)]
+#[derive(Deserialize)]
+pub struct Unit {
+ info: ServiceInfo,
+ config: ServiceConfig,
+}
+
+#[allow(dead_code)]
+impl Restart {
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Restart::Always => "always",
+ Restart::Never => "never",
+ }
+ }
+}
+
+/// Function:
+/// 1. starting services declared in `/etc/vigil/units/`
+/// 2. checks services for dropping and if ts true to restarting it
+/// max restart count: 3
+///
+/// Based on the global runlevel variable, should be runned as second thread
+// TODO: More logs && better errors processing && fix clippy warnings
+pub fn services_mainloop() -> Result<(), Box<dyn std::error::Error + Send>> {
+ let unit_list = parse_all_units()?;
+ let mut pids: Vec<(Child, String, Restart, u8)> = vec![];
+
+ loop {
+ match *RUNLEVEL_STATE.lock().unwrap() {
+ Runlevel::Undefined => {
+ for unit in &unit_list {
+ if unit.config.runlevel == Runlevel::Undefined {
+ let child = std::process::Command::new(unit.config.exec.clone())
+ .spawn()
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
+ pids.push((
+ child,
+ unit.config.exec.clone(),
+ unit.config.restart.clone(),
+ 0,
+ ));
+ }
+ }
+ }
+
+ Runlevel::Reboot => {
+ for unit in &unit_list {
+ if unit.config.runlevel == Runlevel::Reboot {
+ let child = std::process::Command::new(unit.config.exec.clone())
+ .spawn()
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
+ pids.push((
+ child,
+ unit.config.exec.clone(),
+ unit.config.restart.clone(),
+ 0,
+ ));
+ }
+ }
+ }
+
+ Runlevel::Shutdown => {
+ for unit in &unit_list {
+ if unit.config.runlevel == Runlevel::Shutdown {
+ let child = std::process::Command::new(unit.config.exec.clone())
+ .spawn()
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
+ pids.push((
+ child,
+ unit.config.exec.clone(),
+ unit.config.restart.clone(),
+ 0,
+ ));
+ }
+ }
+ }
+
+ Runlevel::OneUser => {
+ for unit in &unit_list {
+ if unit.config.runlevel == Runlevel::OneUser {
+ let child = std::process::Command::new(unit.config.exec.clone())
+ .spawn()
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
+ pids.push((
+ child,
+ unit.config.exec.clone(),
+ unit.config.restart.clone(),
+ 0,
+ ));
+ }
+ }
+ }
+
+ Runlevel::MultiNoNetwork => {
+ for unit in &unit_list {
+ if unit.config.runlevel == Runlevel::MultiNoNetwork {
+ let child = std::process::Command::new(unit.config.exec.clone())
+ .spawn()
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
+ pids.push((
+ child,
+ unit.config.exec.clone(),
+ unit.config.restart.clone(),
+ 0,
+ ));
+ }
+ }
+ }
+
+ Runlevel::MultiNetwork => {
+ for unit in &unit_list {
+ if unit.config.runlevel == Runlevel::MultiNetwork {
+ let child = std::process::Command::new(unit.config.exec.clone())
+ .spawn()
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
+ pids.push((
+ child,
+ unit.config.exec.clone(),
+ unit.config.restart.clone(),
+ 0,
+ ));
+ }
+ }
+ }
+
+ Runlevel::MultiGUINetwork => {
+ for unit in &unit_list {
+ if unit.config.runlevel == Runlevel::MultiGUINetwork {
+ let child = std::process::Command::new(unit.config.exec.clone())
+ .spawn()
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
+ pids.push((
+ child,
+ unit.config.exec.clone(),
+ unit.config.restart.clone(),
+ 0,
+ ));
+ }
+ }
+ }
+ }
+
+ for i in 0..pids.len() {
+ match pids[i].0.try_wait() {
+ Ok(Some(status)) => {
+ if pids[i].2 == Restart::Always && pids[i].3 < 3 {
+ let new_child = std::process::Command::new(pids[i].1.clone())
+ .spawn()
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?;
+ log_warning(&format!(
+ "One of units dropped with Error {};\n Restarting it. Attempt: {}",
+ status,
+ pids[i].3 + 1
+ ));
+
+ pids.push((
+ new_child,
+ pids[i].1.clone(),
+ pids[i].2.clone(),
+ pids[i].3 + 1,
+ ));
+ pids.remove(i);
+ } else {
+ log_warning(&format!(
+ "Unit {} has reached maximum restart attempts or restart policy is not Always, not restarting.",
+ pids[i].1
+ ));
+ continue;
+ }
+ }
+ Ok(None) => continue,
+ Err(e) => log_warning(&format!("Error occurred while checking services: {}", e)),
+ }
+ }
+
+ std::thread::sleep(Duration::from_secs(15));
+ }
+}
+
+fn parse_all_units() -> Result<Vec<Unit>, Box<dyn std::error::Error + Send>> {
+ let mut units: Vec<Unit> = Vec::new();
+
+ for unit in read_dir("/etc/vigil/units")
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?
+ {
+ let unit_path = unit
+ .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send>)?
+ .path();
+ let unit_str: String = match read_to_string(unit_path) {
+ Ok(content) => content,
+ Err(e) => {
+ log_warning(&format!("Error while reading unit: {}", e));
+ continue;
+ }
+ };
+
+ let deserialized: Result<Unit, _> = toml::from_str(&unit_str);
+ match deserialized {
+ Ok(unit) => {
+ units.push(unit);
+ }
+ Err(e) => {
+ log_warning(&format!("Error while parsing unit: {}", e));
+ continue;
+ }
+ };
+ }
+
+ Ok(units)
+}