lib.rs (5340B)
1 #[cfg(feature = "timings")] 2 use std::fs::File; 3 #[cfg(not(feature = "testing"))] 4 use std::io; 5 #[cfg(feature = "timings")] 6 use std::io::Write; 7 #[cfg(not(feature = "testing"))] 8 use std::os::unix::process::CommandExt; 9 #[cfg(not(feature = "testing"))] 10 use std::process::Command; 11 use std::process::ExitCode; 12 use std::thread::sleep; 13 use std::time::Duration; 14 use std::{env, panic, process}; 15 16 #[cfg(not(feature = "testing"))] 17 use log::LevelFilter; 18 #[cfg(feature = "timings")] 19 use log::warn; 20 use log::{error, info}; 21 use nix::sys::reboot::{RebootMode, reboot, set_cad_enabled}; 22 use nix::sys::signal::{SigSet, Signal, kill}; 23 #[cfg(not(feature = "testing"))] 24 use nix::sys::utsname::uname; 25 use nix::unistd::{Pid, sync}; 26 27 #[cfg(feature = "baked-rc")] 28 use baked_rc::*; 29 #[cfg(not(feature = "testing"))] 30 use ev_loop::ev_loop; 31 use kanit_common::constants; 32 use kanit_common::error::{Context, Result}; 33 #[cfg(not(feature = "testing"))] 34 use kanit_diagnostics::Colors; 35 #[cfg(feature = "testing")] 36 use kanit_diagnostics::tap as kanit_tap; 37 use kanit_diagnostics::timing as kanit_timing; 38 #[cfg(feature = "timings")] 39 use kanit_timing::Scope; 40 #[cfg(not(feature = "baked-rc"))] 41 use rc::*; 42 43 #[cfg(feature = "baked-rc")] 44 mod baked_rc; 45 #[cfg(not(feature = "testing"))] 46 mod bsod; 47 #[cfg(not(feature = "testing"))] 48 mod ev_loop; 49 #[cfg(not(feature = "baked-rc"))] 50 mod rc; 51 52 #[cfg(feature = "timings")] 53 fn write_scope(file: &mut File, scope: &Scope) -> Result<()> { 54 let scope_fmt = format!( 55 "{} {} {}\n", 56 scope.name, 57 scope.duration.unwrap_or(Duration::from_secs(0)).as_micros(), 58 scope.level 59 ); 60 61 file.write(scope_fmt.as_bytes()) 62 .context("failed to write scope")?; 63 Ok(()) 64 } 65 66 #[cfg(feature = "timings")] 67 fn write_timing() -> Result<()> { 68 let mut file = File::create(constants::KAN_TIMINGS).context("failed to open times file")?; 69 70 for scope in kanit_timing::get_scopes().iter() { 71 write_scope(&mut file, scope)?; 72 } 73 74 Ok(()) 75 } 76 77 fn initialize() -> Result<()> { 78 let id = kanit_timing::push_scope("initialize"); 79 80 #[cfg(not(feature = "testing"))] 81 let platform = uname() 82 .map(|u| { 83 format!( 84 "{} {} ({})", 85 u.sysname().to_string_lossy(), 86 u.release().to_string_lossy(), 87 u.machine().to_string_lossy() 88 ) 89 }) 90 .unwrap_or_else(|_| "unknown".to_string()); 91 92 #[cfg(not(feature = "testing"))] 93 info!( 94 "== Kanit {}{}{} on {}{}{} ==\n", 95 Colors::BrightGreen, 96 constants::KAN_VERSION, 97 Colors::reset(), 98 Colors::BrightMagenta, 99 platform, 100 Colors::reset() 101 ); 102 103 let mut set = SigSet::all(); 104 set.remove(Signal::SIGCHLD); 105 106 set.thread_block().context("failed to block signals")?; 107 108 set_cad_enabled(false).context("failed to ignore CAD")?; 109 110 // SAFETY: single threaded 111 unsafe { 112 env::set_var("PATH", constants::KAN_PATH); 113 } 114 115 initialize_rc()?; 116 117 kanit_timing::pop_scope(id); 118 119 #[cfg(feature = "timings")] 120 if let Err(e) = write_timing() { 121 warn!("failed to write timings: {e}"); 122 }; 123 124 Ok(()) 125 } 126 127 pub fn teardown(cmd: Option<RebootMode>) -> Result<()> { 128 teardown_rc()?; 129 130 info!("terminating remaining processes"); 131 132 kill(Pid::from_raw(-1), Signal::SIGTERM).context("failed to terminate all processes")?; 133 134 sleep(Duration::from_secs(3)); 135 136 info!("killing remaining processes"); 137 138 kill(Pid::from_raw(-1), Signal::SIGKILL).context("failed to kill all processes")?; 139 140 sync(); 141 142 // reboot will always return an error 143 if let Some(cmd) = cmd { 144 let _ = reboot(cmd); 145 } 146 147 Ok(()) 148 } 149 150 #[cfg(feature = "testing")] 151 fn failure_handle() { 152 kanit_tap::bail::<&str>(None); 153 let _ = reboot(RebootMode::RB_POWER_OFF); 154 } 155 156 #[cfg(not(feature = "testing"))] 157 fn failure_handle() { 158 error!("dropping into emergency shell"); 159 160 let _ = Command::new("sh").exec(); 161 162 error!("failed to drop into emergency shell; good luck o7"); 163 164 loop { 165 // kernel panic if this loop doesn't exist 166 sleep(Duration::from_secs(1)); 167 } 168 } 169 170 pub fn handle_cli() -> ExitCode { 171 if process::id() != 1 { 172 eprintln!("init must be ran as PID 1"); 173 return ExitCode::FAILURE; 174 } 175 176 #[cfg(feature = "testing")] 177 panic::set_hook(Box::new(|info| { 178 kanit_tap::bail(info.payload().downcast_ref::<&str>()); 179 let _ = reboot(RebootMode::RB_POWER_OFF); 180 })); 181 182 #[cfg(not(feature = "testing"))] 183 panic::set_hook(Box::new(|info| { 184 error!("panic detected; tearing down"); 185 let _ = teardown(None); // attempt a teardown 186 bsod::bsod(info); 187 })); 188 189 kanit_diagnostics::tap::header(); 190 191 #[cfg(not(feature = "testing"))] 192 if let Err(e) = kanit_diagnostics::InitializationLogger::init(LevelFilter::Trace) { 193 let _ = write!(&mut io::stdout().lock(), "{e}"); 194 } 195 196 kanit_timing::register(); 197 198 if let Err(e) = initialize() { 199 error!("failed to initialize: {e}"); 200 failure_handle(); 201 } 202 203 #[cfg(not(feature = "testing"))] // no way to test yet 204 if let Err(e) = ev_loop() { 205 error!("event loop failed: {e}"); 206 failure_handle(); 207 } 208 209 #[cfg(feature = "testing")] 210 teardown(Some(RebootMode::RB_POWER_OFF)).expect("failed to unwrap"); 211 212 ExitCode::SUCCESS 213 }