xref: /drstd/src/std/sys/solid/os.rs (revision 0fe3ff0054d3aec7fbf9bddecfecb10bc7d23a51)
1 use super::unsupported;
2 use crate::std::error::Error as StdError;
3 use crate::std::ffi::{CStr, OsStr, OsString};
4 use crate::std::fmt;
5 use crate::std::io;
6 use crate::std::os::{
7     raw::{c_char, c_int},
8     solid::ffi::{OsStrExt, OsStringExt},
9 };
10 use crate::std::path::{self, PathBuf};
11 use crate::std::sync::{PoisonError, RwLock};
12 use crate::std::sys::common::small_c_string::run_with_cstr;
13 use crate::std::vec;
14 use dlibc;
15 
16 use super::{error, itron, memchr};
17 
18 // `solid` directly maps `errno`s to μITRON error codes.
19 impl itron::error::ItronError {
20     #[inline]
21     pub(crate) fn as_io_error(self) -> crate::std::io::Error {
22         crate::std::io::Error::from_raw_os_error(self.as_raw())
23     }
24 }
25 
26 pub fn errno() -> i32 {
27     0
28 }
29 
30 pub fn error_string(errno: i32) -> String {
31     if let Some(name) = error::error_name(errno) {
32         name.to_owned()
33     } else {
34         format!("{errno}")
35     }
36 }
37 
38 pub fn getcwd() -> io::Result<PathBuf> {
39     unsupported()
40 }
41 
42 pub fn chdir(_: &path::Path) -> io::Result<()> {
43     unsupported()
44 }
45 
46 pub struct SplitPaths<'a>(&'a !);
47 
48 pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
49     panic!("unsupported")
50 }
51 
52 impl<'a> Iterator for SplitPaths<'a> {
53     type Item = PathBuf;
54     fn next(&mut self) -> Option<PathBuf> {
55         *self.0
56     }
57 }
58 
59 #[derive(Debug)]
60 pub struct JoinPathsError;
61 
62 pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
63 where
64     I: Iterator<Item = T>,
65     T: AsRef<OsStr>,
66 {
67     Err(JoinPathsError)
68 }
69 
70 impl fmt::Display for JoinPathsError {
71     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72         "not supported on this platform yet".fmt(f)
73     }
74 }
75 
76 impl StdError for JoinPathsError {
77     #[allow(deprecated)]
78     fn description(&self) -> &str {
79         "not supported on this platform yet"
80     }
81 }
82 
83 pub fn current_exe() -> io::Result<PathBuf> {
84     unsupported()
85 }
86 
87 static ENV_LOCK: RwLock<()> = RwLock::new(());
88 
89 pub fn env_read_lock() -> impl Drop {
90     ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
91 }
92 
93 pub struct Env {
94     iter: vec::IntoIter<(OsString, OsString)>,
95 }
96 
97 // FIXME(https://github.com/rust-lang/rust/issues/114583): Remove this when <OsStr as Debug>::fmt matches <str as Debug>::fmt.
98 pub struct EnvStrDebug<'a> {
99     slice: &'a [(OsString, OsString)],
100 }
101 
102 impl fmt::Debug for EnvStrDebug<'_> {
103     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104         let Self { slice } = self;
105         f.debug_list()
106             .entries(
107                 slice
108                     .iter()
109                     .map(|(a, b)| (a.to_str().unwrap(), b.to_str().unwrap())),
110             )
111             .finish()
112     }
113 }
114 
115 impl Env {
116     pub fn str_debug(&self) -> impl fmt::Debug + '_ {
117         let Self { iter } = self;
118         EnvStrDebug {
119             slice: iter.as_slice(),
120         }
121     }
122 }
123 
124 impl fmt::Debug for Env {
125     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126         let Self { iter } = self;
127         f.debug_list().entries(iter.as_slice()).finish()
128     }
129 }
130 
131 impl !Send for Env {}
132 impl !Sync for Env {}
133 
134 impl Iterator for Env {
135     type Item = (OsString, OsString);
136     fn next(&mut self) -> Option<(OsString, OsString)> {
137         self.iter.next()
138     }
139     fn size_hint(&self) -> (usize, Option<usize>) {
140         self.iter.size_hint()
141     }
142 }
143 
144 /// Returns a vector of (variable, value) byte-vector pairs for all the
145 /// environment variables of the current process.
146 pub fn env() -> Env {
147     extern "C" {
148         static mut environ: *const *const c_char;
149     }
150 
151     unsafe {
152         let _guard = env_read_lock();
153         let mut result = Vec::new();
154         if !environ.is_null() {
155             while !(*environ).is_null() {
156                 if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
157                     result.push(key_value);
158                 }
159                 environ = environ.add(1);
160             }
161         }
162         return Env {
163             iter: result.into_iter(),
164         };
165     }
166 
167     fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
168         // Strategy (copied from glibc): Variable name and value are separated
169         // by an ASCII equals sign '='. Since a variable name must not be
170         // empty, allow variable names starting with an equals sign. Skip all
171         // malformed lines.
172         if input.is_empty() {
173             return None;
174         }
175         let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
176         pos.map(|p| {
177             (
178                 OsStringExt::from_vec(input[..p].to_vec()),
179                 OsStringExt::from_vec(input[p + 1..].to_vec()),
180             )
181         })
182     }
183 }
184 
185 pub fn getenv(k: &OsStr) -> Option<OsString> {
186     // environment variables with a nul byte can't be set, so their value is
187     // always None as well
188     run_with_cstr(k.as_bytes(), |k| {
189         let _guard = env_read_lock();
190         let v = unsafe { dlibc::getenv(k.as_ptr()) } as *const dlibc::c_char;
191 
192         if v.is_null() {
193             Ok(None)
194         } else {
195             // SAFETY: `v` cannot be mutated while executing this line since we've a read lock
196             let bytes = unsafe { CStr::from_ptr(v) }.to_bytes().to_vec();
197 
198             Ok(Some(OsStringExt::from_vec(bytes)))
199         }
200     })
201     .ok()
202     .flatten()
203 }
204 
205 pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
206     run_with_cstr(k.as_bytes(), |k| {
207         run_with_cstr(v.as_bytes(), |v| {
208             let _guard = ENV_LOCK.write();
209             cvt_env(unsafe { dlibc::setenv(k.as_ptr(), v.as_ptr(), 1) }).map(drop)
210         })
211     })
212 }
213 
214 pub fn unsetenv(n: &OsStr) -> io::Result<()> {
215     run_with_cstr(n.as_bytes(), |nbuf| {
216         let _guard = ENV_LOCK.write();
217         cvt_env(unsafe { dlibc::unsetenv(nbuf.as_ptr()) }).map(drop)
218     })
219 }
220 
221 /// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
222 /// function just returns a generic error.
223 fn cvt_env(t: c_int) -> io::Result<c_int> {
224     if t == -1 {
225         Err(io::const_io_error!(io::ErrorKind::Uncategorized, "failure"))
226     } else {
227         Ok(t)
228     }
229 }
230 
231 pub fn temp_dir() -> PathBuf {
232     panic!("no standard temporary directory on this platform")
233 }
234 
235 pub fn home_dir() -> Option<PathBuf> {
236     None
237 }
238 
239 pub fn exit(code: i32) -> ! {
240     rtabort!("exit({}) called", code);
241 }
242 
243 pub fn getpid() -> u32 {
244     panic!("no pids on this platform")
245 }
246