From bca3ae435452b22a08eb66b7d3ab98d840a87b94 Mon Sep 17 00:00:00 2001 From: namilsk Date: Fri, 9 Jan 2026 19:13:16 +0300 Subject: Implemetned kernel modules, locales and timezones settings. --- init/Cargo.toml | 2 +- init/src/host/locale.rs | 133 +++++++++++++++++++++++++++++++++++++++++++++ init/src/host/mod.rs | 4 +- init/src/host/set.rs | 53 ++++++++++++++++++ init/src/host/sethn.rs | 37 ------------- init/src/host/timezone.rs | 67 +++++++++++++++++++++++ init/src/kmods/load.rs | 24 ++++++++ init/src/kmods/mod.rs | 1 + init/src/log.rs | 1 - init/src/main.rs | 63 +++++++++------------ init/src/mounts/fstab.rs | 17 ++++-- init/src/mounts/rescue.rs | 3 +- init/src/processes/mod.rs | 1 + init/src/processes/udev.rs | 29 ++++++++++ 14 files changed, 351 insertions(+), 84 deletions(-) create mode 100644 init/src/host/locale.rs create mode 100644 init/src/host/set.rs delete mode 100644 init/src/host/sethn.rs create mode 100644 init/src/host/timezone.rs create mode 100644 init/src/kmods/load.rs create mode 100644 init/src/kmods/mod.rs create mode 100644 init/src/processes/mod.rs create mode 100644 init/src/processes/udev.rs (limited to 'init') diff --git a/init/Cargo.toml b/init/Cargo.toml index a63ffb7..9ff5782 100644 --- a/init/Cargo.toml +++ b/init/Cargo.toml @@ -5,5 +5,5 @@ edition = "2024" description = "Lightweight and stable init system for Anthrill distro based on unix-phylosophy." [dependencies] -errno = "0.3.14" libc = "0.2.178" +liblmod = "0.2.0" diff --git a/init/src/host/locale.rs b/init/src/host/locale.rs new file mode 100644 index 0000000..6dc43b2 --- /dev/null +++ b/init/src/host/locale.rs @@ -0,0 +1,133 @@ +use crate::host::timezone::set_timezone; +use crate::log::{log_success, log_warning}; +use std::fs; +use std::io::Write; +use std::process::Command; + +pub fn set_locale(locale: Option) -> Result<(), Box> { + let loc = match locale { + Some(l) => l, + None => { + if let Ok(content) = fs::read_to_string("/etc/default/locale") { + for line in content.lines() { + if line.starts_with("LANG=") { + let parts: Vec<&str> = line.split('=').collect(); + if parts.len() >= 2 { + let lang_val = parts[1]; + let clean_lang = lang_val.trim_matches('"').trim_matches('\''); + if !clean_lang.is_empty() { + return set_system_locale(clean_lang); + } + } + } + } + } + + if let Ok(lang_env) = std::env::var("LANG") + && !lang_env.is_empty() { + return set_system_locale(&lang_env); + + } + if locale_exists("C.UTF-8") { + "C.UTF-8".to_string() + } else if locale_exists("en_US.UTF-8") { + "en_US.UTF-8".to_string() + } else { + "C".to_string() + } + } + }; + + set_system_locale(&loc) +} +fn set_system_locale(locale: &str) -> Result<(), Box> { + if !locale_exists(locale) { + log_warning(&format!( + "Locale '{}' not found in system, trying alternatives", + locale + )); + + let variants = vec![ + format!("{}.UTF-8", locale), + locale.replace('_', "-"), // Sometimes locales are in form en-US + format!("{}.UTF-8", locale.replace('_', "-")), + ]; + + for variant in variants { + if locale_exists(&variant) { + log_success(&format!("Using alternative locale: {}", variant)); + return set_system_locale_impl(&variant); + } + } + + log_warning("No matching locale found, using C.UTF-8 as fallback"); + return set_system_locale_impl("C.UTF-8"); + } + + set_system_locale_impl(locale) +} + +fn set_system_locale_impl(locale: &str) -> Result<(), Box> { + unsafe { + std::env::set_var("LANG", locale); + std::env::set_var("LC_ALL", locale); + + std::env::set_var("LC_CTYPE", locale); + std::env::set_var("LC_NUMERIC", locale); + std::env::set_var("LC_TIME", locale); + std::env::set_var("LC_COLLATE", locale); + std::env::set_var("LC_MONETARY", locale); + std::env::set_var("LC_MESSAGES", locale); + std::env::set_var("LC_PAPER", locale); + std::env::set_var("LC_NAME", locale); + std::env::set_var("LC_ADDRESS", locale); + std::env::set_var("LC_TELEPHONE", locale); + std::env::set_var("LC_MEASUREMENT", locale); + std::env::set_var("LC_IDENTIFICATION", locale); + } + + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open("/etc/default/locale") + { + writeln!(file, "# Generated by Vigil init system")?; + writeln!(file, "LANG=\"{}\"", locale)?; + writeln!(file, "LC_ALL=\"{}\"", locale)?; + file.flush()?; + } else { + log_warning("Could not write to /etc/default/locale, locale settings may not persist"); + } + + log_success(&format!("Locale set to {}", locale)); + Ok(()) +} + +fn locale_exists(locale: &str) -> bool { + if let Ok(output) = Command::new("locale").arg("-a").output() { + let output_str = String::from_utf8_lossy(&output.stdout); + output_str.lines().any(|line| line.trim() == locale) + } else { + let locale_archive_path = "/usr/lib/locale/locale-archive".to_string(); + let locale_dir_path = "/usr/lib/locale/".to_string(); + + std::path::Path::new(&locale_archive_path).exists() + || std::path::Path::new(&locale_dir_path).exists() + } +} + +pub fn set_system_localization( + timezone: Option, + locale: Option, +) -> Result<(), Box> { + if let Err(e) = set_timezone(timezone) { + log_warning(&format!("Failed to set timezone: {}", e)); + } + + if let Err(e) = set_locale(locale) { + log_warning(&format!("Failed to set locale: {}", e)); + } + + Ok(()) +} diff --git a/init/src/host/mod.rs b/init/src/host/mod.rs index 4a3de53..604c9a7 100644 --- a/init/src/host/mod.rs +++ b/init/src/host/mod.rs @@ -1 +1,3 @@ -pub mod sethn; \ No newline at end of file +pub mod locale; +pub mod set; +pub mod timezone; diff --git a/init/src/host/set.rs b/init/src/host/set.rs new file mode 100644 index 0000000..9550b29 --- /dev/null +++ b/init/src/host/set.rs @@ -0,0 +1,53 @@ +use crate::log::log_warning; +use libc::{c_char, sethostname, size_t}; +use std::{ffi::CString, fs, io}; + +pub fn set_hostname() -> Result<(), Box> { + let content = fs::read_to_string("/etc/hostname") + .map(|s| s.trim().to_string()) + .unwrap_or_else(|_| "localhost".to_string()); + + if let Err(e) = perform_set_hostname(&content) { + match perform_set_hostname("localhost") { + Ok(_) => Err(format!( + "Failed to set hostname: \"{}\": {}. Fell back to localhost successfully.", + content, e + ) + .into()), + + Err(fallback_error) => { + log_warning(&format!( + "Failed to set hostname to '{}': {}. Also failed to set localhost: {}.", + content, e, fallback_error + )); + Err(format!( + "Failed to set hostname '{}' and failed to fall back to localhost: {}.", + content, e + ) + .into()) + } + } + } else { + Ok(()) + } +} + +fn perform_set_hostname(name: &str) -> io::Result<()> { + if name.len() > 64 { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "Hostname too long", + )); + } + + let c_str = CString::new(name) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Hostname contains null byte"))?; + + let res = unsafe { sethostname(c_str.as_ptr() as *const c_char, name.len() as size_t) }; + + if res != 0 { + return Err(io::Error::last_os_error()); + } + + Ok(()) +} diff --git a/init/src/host/sethn.rs b/init/src/host/sethn.rs deleted file mode 100644 index 1857297..0000000 --- a/init/src/host/sethn.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::{ffi::CString, fs, io}; -use libc::{sethostname, c_char, size_t}; -use crate::log::{log_warning}; - -pub fn try_set_hostname() { - let content = fs::read_to_string("/etc/hostname") - .map(|s| s.trim().to_string()) - .unwrap_or_else(|_| "localhost".to_string()); - - if let Err(e) = perform_set_hostname(&content) { - log_warning(&format!("Failed to set hostname to '{}': {}. Falling back to localhost.", content, e)); - let _ = perform_set_hostname("localhost"); - } -} - -fn perform_set_hostname(name: &str) -> io::Result<()> { - if name.len() > 64 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "Hostname too long")); - } - - let c_str = CString::new(name).map_err(|_| { - io::Error::new(io::ErrorKind::InvalidInput, "Hostname contains null byte") - })?; - - let res = unsafe { - sethostname( - c_str.as_ptr() as *const c_char, - name.len() as size_t, - ) - }; - - if res != 0 { - return Err(io::Error::last_os_error()); - } - - Ok(()) -} \ No newline at end of file diff --git a/init/src/host/timezone.rs b/init/src/host/timezone.rs new file mode 100644 index 0000000..2da9e85 --- /dev/null +++ b/init/src/host/timezone.rs @@ -0,0 +1,67 @@ +use crate::log::*; +use std::fs; + +pub fn set_timezone(timezone: Option) -> Result<(), Box> { + let tz = match timezone { + Some(tz) => tz, + None => { + if let Ok(content) = fs::read_to_string("/etc/timezone") { + content.trim().to_string() + } else if let Ok(link_info) = fs::read_link("/etc/localtime") { + let path_str = link_info.to_string_lossy(); + if path_str.starts_with("/usr/share/zoneinfo/") { + let tz_part = path_str.strip_prefix("/usr/share/zoneinfo/").unwrap_or(""); + tz_part.to_string() + } else { + "UTC".to_string() + } + } else { + "UTC".to_string() + } + } + }; + + let tz_file_path = format!("/usr/share/zoneinfo/{}", tz); + + if !std::path::Path::new(&tz_file_path).exists() { + log_warning(&format!( + "Timezone '{}' not found in zoneinfo, using UTC", + tz + )); + let result = set_system_timezone_to_utc(); + if let Err(e) = result { + log_critical_error(&format!("Failed to set timezone to UTC: {}", e)); + return Err(e); + } + return Ok(()); + } + + if std::path::Path::new("/etc/localtime").exists() { + std::fs::remove_file("/etc/localtime")?; + } + + std::os::unix::fs::symlink(&tz_file_path, "/etc/localtime")?; + + log_success(&format!("Timezone set to {}", tz)); + + unsafe { + std::env::set_var("TZ", &tz); + } + + Ok(()) +} + +fn set_system_timezone_to_utc() -> Result<(), Box> { + let utc_tz_path = "/usr/share/zoneinfo/Etc/UTC"; + if std::path::Path::new("/etc/localtime").exists() { + std::fs::remove_file("/etc/localtime")?; + } + + std::os::unix::fs::symlink(utc_tz_path, "/etc/localtime")?; + log_success("Timezone set to UTC"); + unsafe { + std::env::set_var("TZ", "UTC"); + } + + Ok(()) +} diff --git a/init/src/kmods/load.rs b/init/src/kmods/load.rs new file mode 100644 index 0000000..374ce36 --- /dev/null +++ b/init/src/kmods/load.rs @@ -0,0 +1,24 @@ +use liblmod::Selection; +use liblmod::modprobe; +use std::fs; + +fn parse_modules() -> Result, Box> { + let raw_mods = fs::read_to_string("/etc/modules")? + .split(',') + .map(|s| s.trim().to_string()) + .collect(); + + Ok(raw_mods) +} + +pub fn load_modules() -> Result<(), Box> { + let modules = parse_modules()?; + + for i in 0..modules.len() { + modprobe(modules[i].to_string(), "".to_string(), Selection::Current)?; + } + + Ok(()) +} + +// TODO: Think, is `rmmod` needed? diff --git a/init/src/kmods/mod.rs b/init/src/kmods/mod.rs new file mode 100644 index 0000000..f5f538c --- /dev/null +++ b/init/src/kmods/mod.rs @@ -0,0 +1 @@ +pub mod load; diff --git a/init/src/log.rs b/init/src/log.rs index 23f644f..4c89dd1 100644 --- a/init/src/log.rs +++ b/init/src/log.rs @@ -9,4 +9,3 @@ pub fn log_warning(message: &str) { pub fn log_success(message: &str) { println!("\x1b[32m * \x1b[0m {}", message); } - diff --git a/init/src/main.rs b/init/src/main.rs index feea052..7a77e31 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -1,51 +1,29 @@ +mod host; +mod kmods; mod log; mod mounts; mod pid_one; -mod host; +mod processes; -use crate::log::{log_critical_error, log_warning, log_success}; +use crate::host::locale; +use crate::host::set::set_hostname; +use crate::kmods::load::load_modules; +use crate::log::{log_critical_error, log_success, log_warning}; use crate::mounts::fstab::FstabEntry; use crate::mounts::rescue; use crate::pid_one::check_pid; -use crate::host::sethn::try_set_hostname; - -use std::process::Command; +use crate::processes::udev::spawn_udev; // RULE: I will not use .expect() and .unwrap() in this project. This causes panic, // which will affect stability. -fn spawn_udev() -> Result<(), Box> { - log_success("Spawning udev daemon."); - - let udevd_paths = [ - "/sbin/udevd", - "/usr/sbin/udevd", - "/bin/udevd", - "/usr/bin/udevd", - ]; - let udevd_path = udevd_paths - .iter() - .find(|path| std::path::Path::new(path).exists()) - .ok_or("udevd not found in standard locations")?; - - let child = Command::new(udevd_path) - .arg("--daemon") - .spawn() - .map_err(|e| format!("Failed to spawn udevd: {}", e))?; - - log_success(&format!("udevd started with PID: {}", child.id())); - Command::new(udevd_path) - .arg("--trigger") - .output() - .map_err(|e| format!("Failed to trigger udev: {}", e))?; - - Ok(()) -} - fn main() -> Result<(), Box> { println!("Initializing your system."); if let Err(e) = check_pid() { - log_critical_error(&format!("Runned not as first process. init/src/pid_one.rs:8:8 - {}", e)); + log_critical_error(&format!( + "Runned not as first process. init/src/pid_one.rs:8:8 - {}", + e + )); return Err(e); } @@ -67,11 +45,24 @@ fn main() -> Result<(), Box> { } } - let _ = try_set_hostname(); + match set_hostname() { + Ok(_) => log_success("Hostname setted successfully."), + Err(e) => log_warning(&format!("Failed to set hostname: {}", e)), + } + + match load_modules() { + Ok(_) => log_success("Kernel modules loaded successfully."), + Err(e) => log_warning(&format!("Error while loading kernel modules: {}", e)), + } match spawn_udev() { Ok(_) => log_success("Successfully started udev daemon."), - Err(e) => log_critical_error(&format!("Failed to start udev daemon: {}", e)), + Err(e) => log_critical_error(&format!("Something went wrong while spawning udev: {}", e)), + } + + match locale::set_system_localization(None, None) { + Ok(_) => log_success("Localization (timezone and locale) set successfully."), + Err(e) => log_warning(&format!("Failed to set localization: {}", e)), } Ok(()) diff --git a/init/src/mounts/fstab.rs b/init/src/mounts/fstab.rs index c0cfd58..b8708e2 100644 --- a/init/src/mounts/fstab.rs +++ b/init/src/mounts/fstab.rs @@ -1,7 +1,7 @@ -use crate::log::{log_critical_error, log_warning, log_success}; +use crate::log::{log_critical_error, log_success, log_warning}; +use libc::syscall; use std::ffi::CString; use std::{fmt, fs}; -use libc::syscall; #[derive(Debug)] pub struct FstabEntry { @@ -114,15 +114,20 @@ impl FstabEntry { Ok((flags, data)) } - pub fn mount(&self) -> Result<(), Box> { - log_success(&format!("Started mounting {} from {}", self.mountpoint, self.source)); + log_success(&format!( + "Started mounting {} from {}", + self.mountpoint, self.source + )); if self.fstype == "swap" { - log_success(&format!("Filesystem type contains swap, upping it: {}", self.source)); + log_success(&format!( + "Filesystem type contains swap, upping it: {}", + self.source + )); unsafe { - syscall(libc::SYS_swapon, &self.source, 0); + syscall(libc::SYS_swapon, &self.source, 0); } return Ok(()); diff --git a/init/src/mounts/rescue.rs b/init/src/mounts/rescue.rs index ad80a84..078984d 100644 --- a/init/src/mounts/rescue.rs +++ b/init/src/mounts/rescue.rs @@ -31,11 +31,10 @@ pub fn mount_system() -> Result<(), Box> { ); if ret != 0 { - let errno = errno::errno().0; return Err(format!( "Failed to mount {}: {}", target, - std::io::Error::from_raw_os_error(errno) + std::io::Error::last_os_error() ) .into()); } diff --git a/init/src/processes/mod.rs b/init/src/processes/mod.rs new file mode 100644 index 0000000..73b363e --- /dev/null +++ b/init/src/processes/mod.rs @@ -0,0 +1 @@ +pub mod udev; diff --git a/init/src/processes/udev.rs b/init/src/processes/udev.rs new file mode 100644 index 0000000..7cb6a5d --- /dev/null +++ b/init/src/processes/udev.rs @@ -0,0 +1,29 @@ +use std::process::Command; + +// TODO: Rewrite this bad fn +pub fn spawn_udev() -> Result<(), Box> { + let udevd_paths = [ + "/sbin/udevd", + "/usr/sbin/udevd", + "/bin/udevd", + "/usr/bin/udevd", + ]; + let udevd_path = udevd_paths + .iter() + .find(|path| std::path::Path::new(path).exists()) + .ok_or_else(|| -> Box { + "udevd not found in standard locations".into() + })?; + + let mut child = Command::new(udevd_path).arg("--daemon").spawn().map_err( + |e| -> Box { format!("Failed to spawn udevd: {}", e).into() }, + )?; + + child.wait()?; + + Command::new(udevd_path).arg("--trigger").output().map_err( + |e| -> Box { format!("Failed to trigger udev: {}", e).into() }, + )?; + + Ok(()) +} -- cgit v1.2.3