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