Skip to main content

web_admin/
net.rs

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
12/// Implements sleep ms.
13pub 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
23/// Opens listener.
24pub 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
34/// Writes all.
35pub 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
51/// Reads file buf.
52pub 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
84/// Reads file string.
85pub 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
123/// Reads file text.
124pub fn read_file_text(path: &str) -> String {
125    let data = read_file_string(path);
126    String::from_utf8(data).unwrap_or_default()
127}
128
129/// Implements clock gettime ns.
130pub 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}