1use alloc::{string::String, vec::Vec};
2use strat9_syscall::{
3 call,
4 data::{IpcMessage, TimeSpec},
5 flag, number,
6};
7
8const EAGAIN: usize = 11;
9const MAX_EAGAIN_RETRIES: usize = 50;
10const MAX_FILE_READ_BYTES: usize = 128 * 1024;
11
12pub fn sleep_ms(ms: u64) {
14 let req = TimeSpec {
15 tv_sec: (ms / 1000) as i64,
16 tv_nsec: ((ms % 1000) * 1_000_000) as i64,
17 };
18 let _ = unsafe {
19 strat9_syscall::syscall2(number::SYS_NANOSLEEP, &req as *const TimeSpec as usize, 0)
20 };
21}
22
23pub fn open_listener(port: u16) -> usize {
25 let path = alloc::format!("/net/tcp/listen/{}", port);
26 loop {
27 match call::openat(0, &path, flag::OpenFlags::RDWR.bits() as usize, 0) {
28 Ok(fd) => return fd as usize,
29 Err(_) => sleep_ms(200),
30 }
31 }
32}
33
34pub fn write_all(fd: usize, data: &[u8]) -> bool {
36 let mut off = 0usize;
37 while off < data.len() {
38 match call::write(fd, &data[off..]) {
39 Ok(0) => return false,
40 Ok(n) => off += n,
41 Err(e) if e.to_errno() == EAGAIN => {
42 sleep_ms(1);
43 continue;
44 }
45 Err(_) => return false,
46 }
47 }
48 true
49}
50
51pub fn read_file_buf(path: &str, buf: &mut [u8]) -> usize {
53 let fd = match call::openat(0, path, flag::OpenFlags::RDONLY.bits() as usize, 0) {
54 Ok(fd) => fd as usize,
55 Err(_) => return 0,
56 };
57 let mut total = 0;
58 let mut retries = 0;
59 loop {
60 if total >= buf.len() {
61 break;
62 }
63 match call::read(fd, &mut buf[total..]) {
64 Ok(0) => break,
65 Ok(n) => {
66 total += n;
67 retries = 0;
68 }
69 Err(e) if e.to_errno() == EAGAIN => {
70 retries += 1;
71 if retries > MAX_EAGAIN_RETRIES {
72 break;
73 }
74 let _ = call::sched_yield();
75 continue;
76 }
77 Err(_) => break,
78 }
79 }
80 let _ = call::close(fd);
81 total
82}
83
84pub fn read_file_string(path: &str) -> Vec<u8> {
86 let fd = match call::openat(0, path, flag::OpenFlags::RDONLY.bits() as usize, 0) {
87 Ok(fd) => fd as usize,
88 Err(_) => return Vec::new(),
89 };
90 let mut out = Vec::new();
91 let mut chunk = [0u8; 1024];
92 let mut retries = 0;
93 loop {
94 if out.len() >= MAX_FILE_READ_BYTES {
95 break;
96 }
97 match call::read(fd, &mut chunk) {
98 Ok(0) => break,
99 Ok(n) => {
100 let remain = MAX_FILE_READ_BYTES.saturating_sub(out.len());
101 let take = core::cmp::min(n, remain);
102 out.extend_from_slice(&chunk[..take]);
103 if take < n {
104 break;
105 }
106 retries = 0;
107 }
108 Err(e) if e.to_errno() == EAGAIN => {
109 retries += 1;
110 if retries > MAX_EAGAIN_RETRIES {
111 break;
112 }
113 let _ = call::sched_yield();
114 continue;
115 }
116 Err(_) => break,
117 }
118 }
119 let _ = call::close(fd);
120 out
121}
122
123pub fn read_file_text(path: &str) -> String {
125 let data = read_file_string(path);
126 String::from_utf8(data).unwrap_or_default()
127}
128
129pub fn clock_gettime_ns() -> u64 {
131 let mut ts = TimeSpec {
132 tv_sec: 0,
133 tv_nsec: 0,
134 };
135 let _ = call::clock_gettime(1, &mut ts);
136 ts.tv_sec as u64 * 1_000_000_000 + ts.tv_nsec as u64
137}
138
139pub fn ipc_call_path(path: &str, msg_type: u32, payload: &[u8]) -> Option<IpcMessage> {
140 let h = call::ipc_connect(path.as_bytes()).ok()?;
141 let mut req = IpcMessage::new(msg_type);
142 let n = core::cmp::min(payload.len(), req.payload.len());
143 if n > 0 {
144 req.payload[..n].copy_from_slice(&payload[..n]);
145 }
146 if call::ipc_call(h, &mut req).is_err() {
147 let _ = call::handle_close(h);
148 return None;
149 }
150 let _ = call::handle_close(h);
151 Some(req)
152}