strat9_kernel/shell/commands/util/
ntpdate.rs1use super::*;
2use alloc::string::String;
3
4const NTP_PORT: u16 = 123;
5const NTP_UNIX_EPOCH_DELTA: u64 = 2_208_988_800; fn parse_ipv4(s: &str) -> Option<[u8; 4]> {
9 let mut out = [0u8; 4];
10 let mut idx = 0usize;
11 let mut val: u16 = 0;
12 let mut has_digit = false;
13
14 for &b in s.as_bytes() {
15 if b == b'.' {
16 if !has_digit || idx >= 3 || val > 255 {
17 return None;
18 }
19 out[idx] = val as u8;
20 idx += 1;
21 val = 0;
22 has_digit = false;
23 continue;
24 }
25 if !b.is_ascii_digit() {
26 return None;
27 }
28 val = val * 10 + (b - b'0') as u16;
29 has_digit = true;
30 }
31 if !has_digit || idx != 3 || val > 255 {
32 return None;
33 }
34 out[3] = val as u8;
35 Some(out)
36}
37
38fn resolve_ntp_server(server: &str) -> Result<[u8; 4], ShellError> {
40 if let Some(ip) = parse_ipv4(server) {
41 return Ok(ip);
42 }
43
44 let path = alloc::format!("/net/resolve/{}", server);
45 let fd = vfs::open(&path, vfs::OpenFlags::READ).map_err(|_| ShellError::ExecutionFailed)?;
46 let mut buf = [0u8; 64];
47 let n = vfs::read(fd, &mut buf).unwrap_or(0);
48 let _ = vfs::close(fd);
49 if n == 0 {
50 return Err(ShellError::ExecutionFailed);
51 }
52 let end = buf[..n].iter().position(|&b| b == b'\n').unwrap_or(n);
53 let s = core::str::from_utf8(&buf[..end]).unwrap_or("").trim();
54 parse_ipv4(s).ok_or(ShellError::ExecutionFailed)
55}
56
57fn format_ipv4(ip: &[u8; 4]) -> String {
59 alloc::format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3])
60}
61
62fn ntp_query(server_ip: &[u8; 4]) -> Result<(u64, u32), ShellError> {
65 let path = alloc::format!(
66 "/net/udp/connect/{}.{}.{}/{}/{}",
67 server_ip[0],
68 server_ip[1],
69 server_ip[2],
70 server_ip[3],
71 NTP_PORT
72 );
73 let fd = vfs::open(&path, vfs::OpenFlags::RDWR).map_err(|_| ShellError::ExecutionFailed)?;
74
75 let mut req = [0u8; 48];
77 req[0] = 0x23;
78 req[2] = 6; req[3] = 0xEC; let now_ns = crate::syscall::time::current_time_ns();
83 let frac = (((now_ns % 1_000_000_000) as u128) << 32) / 1_000_000_000u128;
84 req[44..48].copy_from_slice(&(frac as u32).to_be_bytes());
85
86 if vfs::write(fd, &req).is_err() {
87 let _ = vfs::close(fd);
88 return Err(ShellError::ExecutionFailed);
89 }
90
91 let start_tick = crate::process::scheduler::ticks();
92 let timeout_ticks = crate::arch::x86_64::timer::TIMER_HZ * 3; let mut resp = [0u8; 64];
94
95 loop {
96 match vfs::read(fd, &mut resp) {
97 Ok(n) if n >= 48 => {
98 let _ = vfs::close(fd);
99 let li_vn_mode = resp[0];
100 let mode = li_vn_mode & 0x07;
101 let stratum = resp[1];
102 if mode != 4 || stratum == 0 {
103 return Err(ShellError::ExecutionFailed);
104 }
105
106 let ntp_secs = u32::from_be_bytes([resp[40], resp[41], resp[42], resp[43]]) as u64;
107 let ntp_frac = u32::from_be_bytes([resp[44], resp[45], resp[46], resp[47]]);
108 if ntp_secs < NTP_UNIX_EPOCH_DELTA {
109 return Err(ShellError::ExecutionFailed);
110 }
111 let unix_secs = ntp_secs - NTP_UNIX_EPOCH_DELTA;
112 let unix_nanos = (((ntp_frac as u128) * 1_000_000_000u128) >> 32) as u32;
113 return Ok((unix_secs, unix_nanos));
114 }
115 Ok(_) => {}
116 Err(_) => {}
117 }
118
119 crate::process::yield_task();
120 let elapsed = crate::process::scheduler::ticks().wrapping_sub(start_tick);
121 if elapsed >= timeout_ticks {
122 let _ = vfs::close(fd);
123 return Err(ShellError::ExecutionFailed);
124 }
125 }
126}
127
128pub fn cmd_ntpdate(args: &[String]) -> Result<(), ShellError> {
129 let server = args.first().map(|s| s.as_str()).unwrap_or("pool.ntp.org");
130 shell_println!("ntpdate: querying {}...", server);
131
132 let server_ip = match resolve_ntp_server(server) {
133 Ok(ip) => ip,
134 Err(_) => {
135 shell_println!(" resolve failed for {}", server);
136 return Err(ShellError::ExecutionFailed);
137 }
138 };
139
140 match ntp_query(&server_ip) {
141 Ok((unix_secs, unix_nanos)) => {
142 shell_println!(" server: {}", format_ipv4(&server_ip));
143 shell_println!(" unix: {}.{:09} UTC", unix_secs, unix_nanos);
144 shell_println!(" note: kernel realtime clock set is not implemented yet");
145 Ok(())
146 }
147 Err(_) => {
148 shell_println!(
149 " no valid NTP response from {} ({})",
150 server,
151 format_ipv4(&server_ip)
152 );
153 Err(ShellError::ExecutionFailed)
154 }
155 }
156}