clock.rs (3627B)
1 use std::path::Path; 2 3 use async_process::{Command, Stdio}; 4 use async_trait::async_trait; 5 use futures_lite::StreamExt; 6 use log::{info, warn}; 7 8 use kanit_common::error::{Context, ErrorKind, Result, StaticError}; 9 use kanit_unit::{Dependencies, Unit}; 10 11 use crate::oneshot::Modules; 12 use crate::unit_name; 13 14 // chronos <3 - Sparkles 15 pub struct Clock; 16 17 fn check_rtc(name: &Path) -> bool { 18 let f_str = name.to_string_lossy(); 19 20 if f_str == "/dev/rtc" 21 || f_str.starts_with("/dev/rtc") 22 && f_str 23 .chars() 24 .nth(8) 25 .map(|c| c.is_ascii_digit()) 26 .unwrap_or(false) 27 { 28 return true; 29 } 30 31 false 32 } 33 34 impl Clock { 35 async fn rtc_exists() -> Result<bool> { 36 Ok(async_fs::read_dir("/dev") 37 .await 38 .context("failed to open /dev")? 39 .filter_map(|res| res.map(|e| e.path()).ok()) 40 .find(|p| check_rtc(p)) 41 .await 42 .is_some()) 43 } 44 } 45 46 const RTC_MODS: [&str; 3] = ["rtc-cmos", "rtc", "genrtc"]; 47 48 #[async_trait] 49 impl Unit for Clock { 50 unit_name!("clock"); 51 52 fn dependencies(&self) -> Dependencies { 53 Dependencies::new().want(Modules.name()).clone() 54 } 55 56 async fn start(&mut self) -> Result<()> { 57 info!("setting time with hardware clock"); 58 59 if !Self::rtc_exists().await? { 60 let mut loaded = false; 61 62 for module in RTC_MODS { 63 let succ = Command::new("modprobe") 64 .args(["-q", module]) 65 .spawn() 66 .context("failed to spawn modprobe")? 67 .status() 68 .await 69 .context("failed to wait")? 70 .success(); 71 72 if succ && Self::rtc_exists().await? { 73 warn!("module {module} should be built in or configured to load"); 74 loaded = true; 75 break; 76 } 77 } 78 79 if !loaded { 80 Err(StaticError("failed to set hardware clock")).kind(ErrorKind::Recoverable)?; 81 } 82 } 83 84 // todo; UTC 85 86 let succ = Command::new("hwclock") 87 .args(["--systz", "--localtime"]) 88 .stderr(Stdio::piped()) 89 .stdout(Stdio::piped()) 90 .spawn() 91 .context("failed to spawn hwclock")? 92 .output() 93 .await 94 .context("failed to wait")? 95 .stderr 96 .is_empty(); 97 98 if !succ { 99 warn!("failed to set system timezone"); 100 } 101 102 let succ = Command::new("hwclock") 103 .args(["--hctosys", "--localtime"]) 104 .stderr(Stdio::piped()) 105 .stdout(Stdio::piped()) 106 .spawn() 107 .context("failed to spawn hwclock")? 108 .output() 109 .await 110 .context("failed to wait")? 111 .stderr 112 .is_empty(); 113 114 if !succ { 115 warn!("failed to set system time"); 116 } 117 118 Ok(()) 119 } 120 121 async fn stop(&mut self) -> Result<()> { 122 info!("setting hardware clock with system time"); 123 124 let succ = Command::new("hwclock") 125 .args(["--systohc", "--localtime"]) 126 .stderr(Stdio::piped()) 127 .stdout(Stdio::piped()) 128 .spawn() 129 .context("failed to spawn hwclock")? 130 .output() 131 .await 132 .context("failed to wait")? 133 .stderr 134 .is_empty(); 135 136 if !succ { 137 warn!("failed to set hardware clock"); 138 } 139 140 Ok(()) 141 } 142 }